You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1032 lines
24 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * Database Forge Class
  41. *
  42. * @category Database
  43. * @author EllisLab Dev Team
  44. * @link https://codeigniter.com/user_guide/database/
  45. */
  46. abstract class CI_DB_forge {
  47. /**
  48. * Database object
  49. *
  50. * @var object
  51. */
  52. protected $db;
  53. /**
  54. * Fields data
  55. *
  56. * @var array
  57. */
  58. public $fields = array();
  59. /**
  60. * Keys data
  61. *
  62. * @var array
  63. */
  64. public $keys = array();
  65. /**
  66. * Primary Keys data
  67. *
  68. * @var array
  69. */
  70. public $primary_keys = array();
  71. /**
  72. * Database character set
  73. *
  74. * @var string
  75. */
  76. public $db_char_set = '';
  77. // --------------------------------------------------------------------
  78. /**
  79. * CREATE DATABASE statement
  80. *
  81. * @var string
  82. */
  83. protected $_create_database = 'CREATE DATABASE %s';
  84. /**
  85. * DROP DATABASE statement
  86. *
  87. * @var string
  88. */
  89. protected $_drop_database = 'DROP DATABASE %s';
  90. /**
  91. * CREATE TABLE statement
  92. *
  93. * @var string
  94. */
  95. protected $_create_table = "%s %s (%s\n)";
  96. /**
  97. * CREATE TABLE IF statement
  98. *
  99. * @var string
  100. */
  101. protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
  102. /**
  103. * CREATE TABLE keys flag
  104. *
  105. * Whether table keys are created from within the
  106. * CREATE TABLE statement.
  107. *
  108. * @var bool
  109. */
  110. protected $_create_table_keys = FALSE;
  111. /**
  112. * DROP TABLE IF EXISTS statement
  113. *
  114. * @var string
  115. */
  116. protected $_drop_table_if = 'DROP TABLE IF EXISTS';
  117. /**
  118. * RENAME TABLE statement
  119. *
  120. * @var string
  121. */
  122. protected $_rename_table = 'ALTER TABLE %s RENAME TO %s;';
  123. /**
  124. * UNSIGNED support
  125. *
  126. * @var bool|array
  127. */
  128. protected $_unsigned = TRUE;
  129. /**
  130. * NULL value representation in CREATE/ALTER TABLE statements
  131. *
  132. * @var string
  133. */
  134. protected $_null = '';
  135. /**
  136. * DEFAULT value representation in CREATE/ALTER TABLE statements
  137. *
  138. * @var string
  139. */
  140. protected $_default = ' DEFAULT ';
  141. // --------------------------------------------------------------------
  142. /**
  143. * Class constructor
  144. *
  145. * @param object &$db Database object
  146. * @return void
  147. */
  148. public function __construct(&$db)
  149. {
  150. $this->db =& $db;
  151. log_message('info', 'Database Forge Class Initialized');
  152. }
  153. // --------------------------------------------------------------------
  154. /**
  155. * Create database
  156. *
  157. * @param string $db_name
  158. * @return bool
  159. */
  160. public function create_database($db_name)
  161. {
  162. if ($this->_create_database === FALSE)
  163. {
  164. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  165. }
  166. elseif ( ! $this->db->query(sprintf($this->_create_database, $this->db->escape_identifiers($db_name), $this->db->char_set, $this->db->dbcollat)))
  167. {
  168. return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
  169. }
  170. if ( ! empty($this->db->data_cache['db_names']))
  171. {
  172. $this->db->data_cache['db_names'][] = $db_name;
  173. }
  174. return TRUE;
  175. }
  176. // --------------------------------------------------------------------
  177. /**
  178. * Drop database
  179. *
  180. * @param string $db_name
  181. * @return bool
  182. */
  183. public function drop_database($db_name)
  184. {
  185. if ($this->_drop_database === FALSE)
  186. {
  187. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  188. }
  189. elseif ( ! $this->db->query(sprintf($this->_drop_database, $this->db->escape_identifiers($db_name))))
  190. {
  191. return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
  192. }
  193. if ( ! empty($this->db->data_cache['db_names']))
  194. {
  195. $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
  196. if ($key !== FALSE)
  197. {
  198. unset($this->db->data_cache['db_names'][$key]);
  199. }
  200. }
  201. return TRUE;
  202. }
  203. // --------------------------------------------------------------------
  204. /**
  205. * Add Key
  206. *
  207. * @param string $key
  208. * @param bool $primary
  209. * @return CI_DB_forge
  210. */
  211. public function add_key($key, $primary = FALSE)
  212. {
  213. // DO NOT change this! This condition is only applicable
  214. // for PRIMARY keys because you can only have one such,
  215. // and therefore all fields you add to it will be included
  216. // in the same, composite PRIMARY KEY.
  217. //
  218. // It's not the same for regular indexes.
  219. if ($primary === TRUE && is_array($key))
  220. {
  221. foreach ($key as $one)
  222. {
  223. $this->add_key($one, $primary);
  224. }
  225. return $this;
  226. }
  227. if ($primary === TRUE)
  228. {
  229. $this->primary_keys[] = $key;
  230. }
  231. else
  232. {
  233. $this->keys[] = $key;
  234. }
  235. return $this;
  236. }
  237. // --------------------------------------------------------------------
  238. /**
  239. * Add Field
  240. *
  241. * @param array $field
  242. * @return CI_DB_forge
  243. */
  244. public function add_field($field)
  245. {
  246. if (is_string($field))
  247. {
  248. if ($field === 'id')
  249. {
  250. $this->add_field(array(
  251. 'id' => array(
  252. 'type' => 'INT',
  253. 'constraint' => 9,
  254. 'auto_increment' => TRUE
  255. )
  256. ));
  257. $this->add_key('id', TRUE);
  258. }
  259. else
  260. {
  261. if (strpos($field, ' ') === FALSE)
  262. {
  263. show_error('Field information is required for that operation.');
  264. }
  265. $this->fields[] = $field;
  266. }
  267. }
  268. if (is_array($field))
  269. {
  270. $this->fields = array_merge($this->fields, $field);
  271. }
  272. return $this;
  273. }
  274. // --------------------------------------------------------------------
  275. /**
  276. * Create Table
  277. *
  278. * @param string $table Table name
  279. * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
  280. * @param array $attributes Associative array of table attributes
  281. * @return bool
  282. */
  283. public function create_table($table, $if_not_exists = FALSE, array $attributes = array())
  284. {
  285. if ($table === '')
  286. {
  287. show_error('A table name is required for that operation.');
  288. }
  289. else
  290. {
  291. $table = $this->db->dbprefix.$table;
  292. }
  293. if (count($this->fields) === 0)
  294. {
  295. show_error('Field information is required.');
  296. }
  297. $sql = $this->_create_table($table, $if_not_exists, $attributes);
  298. if (is_bool($sql))
  299. {
  300. $this->_reset();
  301. if ($sql === FALSE)
  302. {
  303. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  304. }
  305. }
  306. if (($result = $this->db->query($sql)) !== FALSE)
  307. {
  308. isset($this->db->data_cache['table_names']) && $this->db->data_cache['table_names'][] = $table;
  309. // Most databases don't support creating indexes from within the CREATE TABLE statement
  310. if ( ! empty($this->keys))
  311. {
  312. for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
  313. {
  314. $this->db->query($sqls[$i]);
  315. }
  316. }
  317. }
  318. $this->_reset();
  319. return $result;
  320. }
  321. // --------------------------------------------------------------------
  322. /**
  323. * Create Table
  324. *
  325. * @param string $table Table name
  326. * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
  327. * @param array $attributes Associative array of table attributes
  328. * @return mixed
  329. */
  330. protected function _create_table($table, $if_not_exists, $attributes)
  331. {
  332. if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
  333. {
  334. if ($this->db->table_exists($table))
  335. {
  336. return TRUE;
  337. }
  338. else
  339. {
  340. $if_not_exists = FALSE;
  341. }
  342. }
  343. $sql = ($if_not_exists)
  344. ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
  345. : 'CREATE TABLE';
  346. $columns = $this->_process_fields(TRUE);
  347. for ($i = 0, $c = count($columns); $i < $c; $i++)
  348. {
  349. $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
  350. ? "\n\t".$columns[$i]['_literal']
  351. : "\n\t".$this->_process_column($columns[$i]);
  352. }
  353. $columns = implode(',', $columns)
  354. .$this->_process_primary_keys($table);
  355. // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
  356. if ($this->_create_table_keys === TRUE)
  357. {
  358. $columns .= $this->_process_indexes($table);
  359. }
  360. // _create_table will usually have the following format: "%s %s (%s\n)"
  361. $sql = sprintf($this->_create_table.'%s',
  362. $sql,
  363. $this->db->escape_identifiers($table),
  364. $columns,
  365. $this->_create_table_attr($attributes)
  366. );
  367. return $sql;
  368. }
  369. // --------------------------------------------------------------------
  370. /**
  371. * CREATE TABLE attributes
  372. *
  373. * @param array $attributes Associative array of table attributes
  374. * @return string
  375. */
  376. protected function _create_table_attr($attributes)
  377. {
  378. $sql = '';
  379. foreach (array_keys($attributes) as $key)
  380. {
  381. if (is_string($key))
  382. {
  383. $sql .= ' '.strtoupper($key).' '.$attributes[$key];
  384. }
  385. }
  386. return $sql;
  387. }
  388. // --------------------------------------------------------------------
  389. /**
  390. * Drop Table
  391. *
  392. * @param string $table_name Table name
  393. * @param bool $if_exists Whether to add an IF EXISTS condition
  394. * @return bool
  395. */
  396. public function drop_table($table_name, $if_exists = FALSE)
  397. {
  398. if ($table_name === '')
  399. {
  400. return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
  401. }
  402. if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
  403. {
  404. return TRUE;
  405. }
  406. $query = $this->db->query($query);
  407. // Update table list cache
  408. if ($query && ! empty($this->db->data_cache['table_names']))
  409. {
  410. $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
  411. if ($key !== FALSE)
  412. {
  413. unset($this->db->data_cache['table_names'][$key]);
  414. }
  415. }
  416. return $query;
  417. }
  418. // --------------------------------------------------------------------
  419. /**
  420. * Drop Table
  421. *
  422. * Generates a platform-specific DROP TABLE string
  423. *
  424. * @param string $table Table name
  425. * @param bool $if_exists Whether to add an IF EXISTS condition
  426. * @return mixed (Returns a platform-specific DROP table string, or TRUE to indicate there's nothing to do)
  427. */
  428. protected function _drop_table($table, $if_exists)
  429. {
  430. $sql = 'DROP TABLE';
  431. if ($if_exists)
  432. {
  433. if ($this->_drop_table_if === FALSE)
  434. {
  435. if ( ! $this->db->table_exists($table))
  436. {
  437. return TRUE;
  438. }
  439. }
  440. else
  441. {
  442. $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
  443. }
  444. }
  445. return $sql.' '.$this->db->escape_identifiers($table);
  446. }
  447. // --------------------------------------------------------------------
  448. /**
  449. * Rename Table
  450. *
  451. * @param string $table_name Old table name
  452. * @param string $new_table_name New table name
  453. * @return bool
  454. */
  455. public function rename_table($table_name, $new_table_name)
  456. {
  457. if ($table_name === '' OR $new_table_name === '')
  458. {
  459. show_error('A table name is required for that operation.');
  460. return FALSE;
  461. }
  462. elseif ($this->_rename_table === FALSE)
  463. {
  464. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  465. }
  466. $result = $this->db->query(sprintf($this->_rename_table,
  467. $this->db->escape_identifiers($this->db->dbprefix.$table_name),
  468. $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
  469. );
  470. if ($result && ! empty($this->db->data_cache['table_names']))
  471. {
  472. $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
  473. if ($key !== FALSE)
  474. {
  475. $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
  476. }
  477. }
  478. return $result;
  479. }
  480. // --------------------------------------------------------------------
  481. /**
  482. * Column Add
  483. *
  484. * @todo Remove deprecated $_after option in 3.1+
  485. * @param string $table Table name
  486. * @param array $field Column definition
  487. * @param string $_after Column for AFTER clause (deprecated)
  488. * @return bool
  489. */
  490. public function add_column($table, $field, $_after = NULL)
  491. {
  492. // Work-around for literal column definitions
  493. is_array($field) OR $field = array($field);
  494. foreach (array_keys($field) as $k)
  495. {
  496. // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
  497. if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
  498. {
  499. $field[$k]['after'] = $_after;
  500. }
  501. $this->add_field(array($k => $field[$k]));
  502. }
  503. $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
  504. $this->_reset();
  505. if ($sqls === FALSE)
  506. {
  507. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  508. }
  509. for ($i = 0, $c = count($sqls); $i < $c; $i++)
  510. {
  511. if ($this->db->query($sqls[$i]) === FALSE)
  512. {
  513. return FALSE;
  514. }
  515. }
  516. return TRUE;
  517. }
  518. // --------------------------------------------------------------------
  519. /**
  520. * Column Drop
  521. *
  522. * @param string $table Table name
  523. * @param string $column_name Column name
  524. * @return bool
  525. */
  526. public function drop_column($table, $column_name)
  527. {
  528. $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
  529. if ($sql === FALSE)
  530. {
  531. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  532. }
  533. return $this->db->query($sql);
  534. }
  535. // --------------------------------------------------------------------
  536. /**
  537. * Column Modify
  538. *
  539. * @param string $table Table name
  540. * @param string $field Column definition
  541. * @return bool
  542. */
  543. public function modify_column($table, $field)
  544. {
  545. // Work-around for literal column definitions
  546. is_array($field) OR $field = array($field);
  547. foreach (array_keys($field) as $k)
  548. {
  549. $this->add_field(array($k => $field[$k]));
  550. }
  551. if (count($this->fields) === 0)
  552. {
  553. show_error('Field information is required.');
  554. }
  555. $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
  556. $this->_reset();
  557. if ($sqls === FALSE)
  558. {
  559. return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
  560. }
  561. for ($i = 0, $c = count($sqls); $i < $c; $i++)
  562. {
  563. if ($this->db->query($sqls[$i]) === FALSE)
  564. {
  565. return FALSE;
  566. }
  567. }
  568. return TRUE;
  569. }
  570. // --------------------------------------------------------------------
  571. /**
  572. * ALTER TABLE
  573. *
  574. * @param string $alter_type ALTER type
  575. * @param string $table Table name
  576. * @param mixed $field Column definition
  577. * @return string|string[]
  578. */
  579. protected function _alter_table($alter_type, $table, $field)
  580. {
  581. $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
  582. // DROP has everything it needs now.
  583. if ($alter_type === 'DROP')
  584. {
  585. return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
  586. }
  587. $sql .= ($alter_type === 'ADD')
  588. ? 'ADD '
  589. : $alter_type.' COLUMN ';
  590. $sqls = array();
  591. for ($i = 0, $c = count($field); $i < $c; $i++)
  592. {
  593. $sqls[] = $sql
  594. .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
  595. }
  596. return $sqls;
  597. }
  598. // --------------------------------------------------------------------
  599. /**
  600. * Process fields
  601. *
  602. * @param bool $create_table
  603. * @return array
  604. */
  605. protected function _process_fields($create_table = FALSE)
  606. {
  607. $fields = array();
  608. foreach ($this->fields as $key => $attributes)
  609. {
  610. if (is_int($key) && ! is_array($attributes))
  611. {
  612. $fields[] = array('_literal' => $attributes);
  613. continue;
  614. }
  615. $attributes = array_change_key_case($attributes, CASE_UPPER);
  616. if ($create_table === TRUE && empty($attributes['TYPE']))
  617. {
  618. continue;
  619. }
  620. isset($attributes['TYPE']) && $this->_attr_type($attributes);
  621. $field = array(
  622. 'name' => $key,
  623. 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
  624. 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
  625. 'length' => '',
  626. 'unsigned' => '',
  627. 'null' => '',
  628. 'unique' => '',
  629. 'default' => '',
  630. 'auto_increment' => '',
  631. '_literal' => FALSE
  632. );
  633. isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
  634. if ($create_table === FALSE)
  635. {
  636. if (isset($attributes['AFTER']))
  637. {
  638. $field['after'] = $attributes['AFTER'];
  639. }
  640. elseif (isset($attributes['FIRST']))
  641. {
  642. $field['first'] = (bool) $attributes['FIRST'];
  643. }
  644. }
  645. $this->_attr_default($attributes, $field);
  646. if (isset($attributes['NULL']))
  647. {
  648. if ($attributes['NULL'] === TRUE)
  649. {
  650. $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
  651. }
  652. else
  653. {
  654. $field['null'] = ' NOT NULL';
  655. }
  656. }
  657. elseif ($create_table === TRUE)
  658. {
  659. $field['null'] = ' NOT NULL';
  660. }
  661. $this->_attr_auto_increment($attributes, $field);
  662. $this->_attr_unique($attributes, $field);
  663. if (isset($attributes['COMMENT']))
  664. {
  665. $field['comment'] = $this->db->escape($attributes['COMMENT']);
  666. }
  667. if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
  668. {
  669. switch (strtoupper($attributes['TYPE']))
  670. {
  671. case 'ENUM':
  672. case 'SET':
  673. $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
  674. default:
  675. $field['length'] = is_array($attributes['CONSTRAINT'])
  676. ? '('.implode(',', $attributes['CONSTRAINT']).')'
  677. : '('.$attributes['CONSTRAINT'].')';
  678. break;
  679. }
  680. }
  681. $fields[] = $field;
  682. }
  683. return $fields;
  684. }
  685. // --------------------------------------------------------------------
  686. /**
  687. * Process column
  688. *
  689. * @param array $field
  690. * @return string
  691. */
  692. protected function _process_column($field)
  693. {
  694. return $this->db->escape_identifiers($field['name'])
  695. .' '.$field['type'].$field['length']
  696. .$field['unsigned']
  697. .$field['default']
  698. .$field['null']
  699. .$field['auto_increment']
  700. .$field['unique'];
  701. }
  702. // --------------------------------------------------------------------
  703. /**
  704. * Field attribute TYPE
  705. *
  706. * Performs a data type mapping between different databases.
  707. *
  708. * @param array &$attributes
  709. * @return void
  710. */
  711. protected function _attr_type(&$attributes)
  712. {
  713. // Usually overridden by drivers
  714. }
  715. // --------------------------------------------------------------------
  716. /**
  717. * Field attribute UNSIGNED
  718. *
  719. * Depending on the _unsigned property value:
  720. *
  721. * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
  722. * - FALSE will always set $field['unsigned'] to ''
  723. * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
  724. * if $attributes['TYPE'] is found in the array
  725. * - array(TYPE => UTYPE) will change $field['type'],
  726. * from TYPE to UTYPE in case of a match
  727. *
  728. * @param array &$attributes
  729. * @param array &$field
  730. * @return void
  731. */
  732. protected function _attr_unsigned(&$attributes, &$field)
  733. {
  734. if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
  735. {
  736. return;
  737. }
  738. // Reset the attribute in order to avoid issues if we do type conversion
  739. $attributes['UNSIGNED'] = FALSE;
  740. if (is_array($this->_unsigned))
  741. {
  742. foreach (array_keys($this->_unsigned) as $key)
  743. {
  744. if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
  745. {
  746. $field['unsigned'] = ' UNSIGNED';
  747. return;
  748. }
  749. elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
  750. {
  751. $field['type'] = $key;
  752. return;
  753. }
  754. }
  755. return;
  756. }
  757. $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
  758. }
  759. // --------------------------------------------------------------------
  760. /**
  761. * Field attribute DEFAULT
  762. *
  763. * @param array &$attributes
  764. * @param array &$field
  765. * @return void
  766. */
  767. protected function _attr_default(&$attributes, &$field)
  768. {
  769. if ($this->_default === FALSE)
  770. {
  771. return;
  772. }
  773. if (array_key_exists('DEFAULT', $attributes))
  774. {
  775. if ($attributes['DEFAULT'] === NULL)
  776. {
  777. $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
  778. // Override the NULL attribute if that's our default
  779. $attributes['NULL'] = TRUE;
  780. $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
  781. }
  782. else
  783. {
  784. $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
  785. }
  786. }
  787. }
  788. // --------------------------------------------------------------------
  789. /**
  790. * Field attribute UNIQUE
  791. *
  792. * @param array &$attributes
  793. * @param array &$field
  794. * @return void
  795. */
  796. protected function _attr_unique(&$attributes, &$field)
  797. {
  798. if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
  799. {
  800. $field['unique'] = ' UNIQUE';
  801. }
  802. }
  803. // --------------------------------------------------------------------
  804. /**
  805. * Field attribute AUTO_INCREMENT
  806. *
  807. * @param array &$attributes
  808. * @param array &$field
  809. * @return void
  810. */
  811. protected function _attr_auto_increment(&$attributes, &$field)
  812. {
  813. if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
  814. {
  815. $field['auto_increment'] = ' AUTO_INCREMENT';
  816. }
  817. }
  818. // --------------------------------------------------------------------
  819. /**
  820. * Process primary keys
  821. *
  822. * @param string $table Table name
  823. * @return string
  824. */
  825. protected function _process_primary_keys($table)
  826. {
  827. $sql = '';
  828. for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
  829. {
  830. if ( ! isset($this->fields[$this->primary_keys[$i]]))
  831. {
  832. unset($this->primary_keys[$i]);
  833. }
  834. }
  835. if (count($this->primary_keys) > 0)
  836. {
  837. $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
  838. .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
  839. }
  840. return $sql;
  841. }
  842. // --------------------------------------------------------------------
  843. /**
  844. * Process indexes
  845. *
  846. * @param string $table Table name
  847. * @return string[] list of SQL statements
  848. */
  849. protected function _process_indexes($table)
  850. {
  851. $sqls = array();
  852. for ($i = 0, $c = count($this->keys); $i < $c; $i++)
  853. {
  854. if (is_array($this->keys[$i]))
  855. {
  856. for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
  857. {
  858. if ( ! isset($this->fields[$this->keys[$i][$i2]]))
  859. {
  860. unset($this->keys[$i][$i2]);
  861. continue;
  862. }
  863. }
  864. }
  865. elseif ( ! isset($this->fields[$this->keys[$i]]))
  866. {
  867. unset($this->keys[$i]);
  868. continue;
  869. }
  870. is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
  871. $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
  872. .' ON '.$this->db->escape_identifiers($table)
  873. .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
  874. }
  875. return $sqls;
  876. }
  877. // --------------------------------------------------------------------
  878. /**
  879. * Reset
  880. *
  881. * Resets table creation vars
  882. *
  883. * @return void
  884. */
  885. protected function _reset()
  886. {
  887. $this->fields = $this->keys = $this->primary_keys = array();
  888. }
  889. }