blob: 5c782f8756a9da4658c679c679f0560d69514c26 [file] [log] [blame]
Andrey Andreevc5536aa2012-11-01 17:33:58 +02001<?php
Derek Allard2067d1a2008-11-13 22:59:24 +00002/**
Derek Jonesf4a4bd82011-10-20 12:18:42 -05003 * CodeIgniter
Derek Allard2067d1a2008-11-13 22:59:24 +00004 *
Phil Sturgeon07c1ac82012-03-09 17:03:37 +00005 * An open source application development framework for PHP 5.2.4 or newer
Derek Allard2067d1a2008-11-13 22:59:24 +00006 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05007 * NOTICE OF LICENSE
Andrey Andreev24276a32012-01-08 02:44:38 +02008 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -05009 * Licensed under the Open Software License version 3.0
Andrey Andreev24276a32012-01-08 02:44:38 +020010 *
Derek Jonesf4a4bd82011-10-20 12:18:42 -050011 * This source file is subject to the Open Software License (OSL 3.0) that is
12 * bundled with this package in the files license.txt / license.rst. It is
13 * also available through the world wide web at this URL:
14 * http://opensource.org/licenses/OSL-3.0
15 * If you did not receive a copy of the license and are unable to obtain it
16 * through the world wide web, please send an email to
17 * licensing@ellislab.com so we can send you a copy immediately.
18 *
Derek Allard2067d1a2008-11-13 22:59:24 +000019 * @package CodeIgniter
Derek Jonesf4a4bd82011-10-20 12:18:42 -050020 * @author EllisLab Dev Team
Greg Aker0defe5d2012-01-01 18:46:41 -060021 * @copyright Copyright (c) 2008 - 2012, EllisLab, Inc. (http://ellislab.com/)
Derek Jonesf4a4bd82011-10-20 12:18:42 -050022 * @license http://opensource.org/licenses/OSL-3.0 Open Software License (OSL 3.0)
Derek Allard2067d1a2008-11-13 22:59:24 +000023 * @link http://codeigniter.com
24 * @since Version 1.0
25 * @filesource
26 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020027defined('BASEPATH') OR exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +000028
Derek Allard2067d1a2008-11-13 22:59:24 +000029/**
Andrey Andreevd947eba2012-04-09 14:58:28 +030030 * Database Forge Class
Derek Allard2067d1a2008-11-13 22:59:24 +000031 *
32 * @category Database
Derek Jonesf4a4bd82011-10-20 12:18:42 -050033 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000034 * @link http://codeigniter.com/user_guide/database/
35 */
Timothy Warren833d5042012-03-19 16:12:03 -040036abstract class CI_DB_forge {
Derek Allard2067d1a2008-11-13 22:59:24 +000037
Andrey Andreevae85eb42012-11-02 01:42:31 +020038 /**
Andrey Andreeva287a342012-11-05 23:19:59 +020039 * Database object
40 *
41 * @var object
42 */
Andrey Andreeveaa60c72012-11-06 01:11:22 +020043 protected $db;
Andrey Andreeva287a342012-11-05 23:19:59 +020044
45 /**
Andrey Andreevae85eb42012-11-02 01:42:31 +020046 * Fields data
47 *
48 * @var array
49 */
Andrey Andreev24276a32012-01-08 02:44:38 +020050 public $fields = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020051
52 /**
53 * Keys data
54 *
55 * @var array
56 */
Andrey Andreev24276a32012-01-08 02:44:38 +020057 public $keys = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020058
59 /**
60 * Primary Keys data
61 *
62 * @var array
63 */
Andrey Andreev24276a32012-01-08 02:44:38 +020064 public $primary_keys = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020065
66 /**
67 * Database character set
68 *
69 * @var string
70 */
Andrey Andreev5fd3ae82012-10-24 14:55:35 +030071 public $db_char_set = '';
Derek Allard2067d1a2008-11-13 22:59:24 +000072
Andrey Andreevae85eb42012-11-02 01:42:31 +020073 // --------------------------------------------------------------------
74
75 /**
76 * CREATE DATABASE statement
77 *
78 * @var string
79 */
Andrey Andreevd947eba2012-04-09 14:58:28 +030080 protected $_create_database = 'CREATE DATABASE %s';
Andrey Andreevae85eb42012-11-02 01:42:31 +020081
82 /**
83 * DROP DATABASE statement
84 *
85 * @var string
86 */
Andrey Andreevd947eba2012-04-09 14:58:28 +030087 protected $_drop_database = 'DROP DATABASE %s';
Andrey Andreevae85eb42012-11-02 01:42:31 +020088
89 /**
Andrey Andreeva287a342012-11-05 23:19:59 +020090 * CREATE TABLE statement
Andrey Andreevae85eb42012-11-02 01:42:31 +020091 *
92 * @var string
93 */
Andrey Andreeva287a342012-11-05 23:19:59 +020094 protected $_create_table = "%s %s (%s\n)";
95
96 /**
97 * CREATE TABLE IF statement
98 *
99 * @var string
100 */
101 protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
102
103 /**
104 * CREATE TABLE keys flag
105 *
106 * Whether table keys are created from within the
107 * CREATE TABLE statement.
108 *
109 * @var bool
110 */
111 protected $_create_table_keys = FALSE;
112
113 /**
114 * DROP TABLE IF EXISTS statement
115 *
116 * @var string
117 */
118 protected $_drop_table_if = 'DROP TABLE IF EXISTS';
Andrey Andreevae85eb42012-11-02 01:42:31 +0200119
120 /**
121 * RENAME TABLE statement
122 *
123 * @var string
124 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200125 protected $_rename_table = 'ALTER TABLE %s RENAME TO %s;';
126
127 /**
128 * UNSIGNED support
129 *
130 * @var bool|array
131 */
132 protected $_unsigned = TRUE;
133
134 /**
135 * NULL value representatin in CREATE/ALTER TABLE statements
136 *
137 * @var string
138 */
139 protected $_null = '';
140
141 /**
142 * DEFAULT value representation in CREATE/ALTER TABLE statements
143 *
144 * @var string
145 */
146 protected $_default = ' DEFAULT ';
Andrey Andreevd947eba2012-04-09 14:58:28 +0300147
Andrey Andreevae85eb42012-11-02 01:42:31 +0200148 // --------------------------------------------------------------------
149
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300150 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200151 * Class constructor
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300152 *
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200153 * @param object &$db Database object
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300154 * @return void
155 */
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200156 public function __construct(&$db)
Derek Allard2067d1a2008-11-13 22:59:24 +0000157 {
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200158 $this->db =& $db;
Andrey Andreev24276a32012-01-08 02:44:38 +0200159 log_message('debug', 'Database Forge Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000160 }
161
162 // --------------------------------------------------------------------
163
164 /**
165 * Create database
166 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200167 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000168 * @return bool
169 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200170 public function create_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000171 {
Andrey Andreevd947eba2012-04-09 14:58:28 +0300172 if ($this->_create_database === FALSE)
173 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200174 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300175 }
176 elseif ( ! $this->db->query(sprintf($this->_create_database, $db_name, $this->db->char_set, $this->db->dbcollat)))
177 {
178 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
179 }
180
Andrey Andreev5d281762012-06-11 22:05:40 +0300181 if ( ! empty($this->db->data_cache['db_names']))
182 {
183 $this->db->data_cache['db_names'][] = $db_name;
184 }
185
Andrey Andreevd947eba2012-04-09 14:58:28 +0300186 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000187 }
188
189 // --------------------------------------------------------------------
190
191 /**
192 * Drop database
193 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200194 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000195 * @return bool
196 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200197 public function drop_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000198 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100199 if ($db_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300200 {
201 show_error('A table name is required for that operation.');
202 return FALSE;
203 }
204 elseif ($this->_drop_database === FALSE)
205 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200206 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300207 }
208 elseif ( ! $this->db->query(sprintf($this->_drop_database, $db_name)))
209 {
210 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
211 }
212
Andrey Andreev5d281762012-06-11 22:05:40 +0300213 if ( ! empty($this->db->data_cache['db_names']))
214 {
215 $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
216 if ($key !== FALSE)
217 {
218 unset($this->db->data_cache['db_names'][$key]);
219 }
220 }
221
Andrey Andreevd947eba2012-04-09 14:58:28 +0300222 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000223 }
224
225 // --------------------------------------------------------------------
226
227 /**
228 * Add Key
229 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200230 * @param string $key
231 * @param bool $primary
Andrey Andreevd8dba5d2012-12-17 15:42:01 +0200232 * @return CI_DB_forge
Derek Allard2067d1a2008-11-13 22:59:24 +0000233 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000234 public function add_key($key = '', $primary = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000235 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200236 if (empty($key))
237 {
238 show_error('Key information is required for that operation.');
239 }
240
Andrey Andreevd743cdb2012-11-06 00:00:01 +0200241 if ($primary === TRUE && is_array($key))
Derek Allard2067d1a2008-11-13 22:59:24 +0000242 {
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500243 foreach ($key as $one)
Derek Allard2067d1a2008-11-13 22:59:24 +0000244 {
245 $this->add_key($one, $primary);
246 }
Barry Mienydd671972010-10-04 16:33:58 +0200247
Andrey Andreeva287a342012-11-05 23:19:59 +0200248 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000249 }
Barry Mienydd671972010-10-04 16:33:58 +0200250
Derek Allard2067d1a2008-11-13 22:59:24 +0000251 if ($primary === TRUE)
252 {
253 $this->primary_keys[] = $key;
254 }
255 else
256 {
257 $this->keys[] = $key;
258 }
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000259
260 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000261 }
262
263 // --------------------------------------------------------------------
264
265 /**
266 * Add Field
267 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200268 * @param array $field
Andrew Podner4296a652012-12-17 07:51:15 -0500269 * @return CI_DB_forge
Derek Allard2067d1a2008-11-13 22:59:24 +0000270 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000271 public function add_field($field = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000272 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200273 if (empty($field))
Derek Allard2067d1a2008-11-13 22:59:24 +0000274 {
275 show_error('Field information is required.');
276 }
Barry Mienydd671972010-10-04 16:33:58 +0200277
Derek Allard2067d1a2008-11-13 22:59:24 +0000278 if (is_string($field))
279 {
Andrey Andreev24276a32012-01-08 02:44:38 +0200280 if ($field === 'id')
Derek Allard2067d1a2008-11-13 22:59:24 +0000281 {
282 $this->add_field(array(
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000283 'id' => array(
284 'type' => 'INT',
285 'constraint' => 9,
286 'auto_increment' => TRUE
287 )
288 ));
Derek Allard2067d1a2008-11-13 22:59:24 +0000289 $this->add_key('id', TRUE);
290 }
291 else
292 {
293 if (strpos($field, ' ') === FALSE)
294 {
295 show_error('Field information is required for that operation.');
296 }
Barry Mienydd671972010-10-04 16:33:58 +0200297
Derek Allard2067d1a2008-11-13 22:59:24 +0000298 $this->fields[] = $field;
299 }
300 }
Barry Mienydd671972010-10-04 16:33:58 +0200301
Derek Allard2067d1a2008-11-13 22:59:24 +0000302 if (is_array($field))
303 {
304 $this->fields = array_merge($this->fields, $field);
305 }
Andrey Andreev24276a32012-01-08 02:44:38 +0200306
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000307 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000308 }
309
310 // --------------------------------------------------------------------
311
312 /**
313 * Create Table
314 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200315 * @param string $table Table name
316 * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000317 * @return bool
318 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000319 public function create_table($table = '', $if_not_exists = FALSE)
Barry Mienydd671972010-10-04 16:33:58 +0200320 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100321 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000322 {
323 show_error('A table name is required for that operation.');
324 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200325 else
326 {
327 $table = $this->db->dbprefix.$table;
328 }
Barry Mienydd671972010-10-04 16:33:58 +0200329
Andrey Andreev24276a32012-01-08 02:44:38 +0200330 if (count($this->fields) === 0)
Barry Mienydd671972010-10-04 16:33:58 +0200331 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000332 show_error('Field information is required.');
333 }
334
Andrey Andreeva287a342012-11-05 23:19:59 +0200335 $sql = $this->_create_table($table, $if_not_exists);
Andrey Andreev5d281762012-06-11 22:05:40 +0300336
337 if (is_bool($sql))
338 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200339 $this->_reset();
340 if ($sql === FALSE)
341 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200342 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200343 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300344 }
345
Andrey Andreeva287a342012-11-05 23:19:59 +0200346 if (($result = $this->db->query($sql)) !== FALSE)
Andrey Andreev5d281762012-06-11 22:05:40 +0300347 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200348 empty($this->db->data_cache['table_names']) OR $this->db->data_cache['table_names'][] = $table;
349
350 // Most databases don't support creating indexes from within the CREATE TABLE statement
351 if ( ! empty($this->keys))
352 {
353 for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
354 {
355 $this->db->query($sqls[$i]);
356 }
357 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300358 }
359
Andrey Andreeva287a342012-11-05 23:19:59 +0200360 $this->_reset();
Andrey Andreev5d281762012-06-11 22:05:40 +0300361 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000362 }
363
364 // --------------------------------------------------------------------
365
366 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200367 * Create Table
368 *
369 * @param string $table Table name
370 * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
371 * @return mixed
372 */
373 protected function _create_table($table, $if_not_exists)
374 {
375 if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
376 {
377 if ($this->db->table_exists($table))
378 {
379 return TRUE;
380 }
381 else
382 {
383 $if_not_exists = FALSE;
384 }
385 }
386
387 $sql = ($if_not_exists)
388 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
389 : 'CREATE TABLE';
390
391 $columns = $this->_process_fields(TRUE);
392 for ($i = 0, $c = count($columns); $i < $c; $i++)
393 {
394 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
395 ? "\n\t".$columns[$i]['_literal']
396 : "\n\t".$this->_process_column($columns[$i]);
397 }
398
399 $columns = implode(',', $columns)
400 .$this->_process_primary_keys($table);
401
402 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
403 if ($this->_create_table_keys === TRUE)
404 {
Andrey Andreev35451022012-11-25 17:20:04 +0200405 $columns .= $this->_process_indexes($table);
Andrey Andreeva287a342012-11-05 23:19:59 +0200406 }
407
408 // _create_table will usually have the following format: "%s %s (%s\n)"
409 $sql = sprintf($this->_create_table.';',
410 $sql,
411 $this->db->escape_identifiers($table),
412 $columns
413 );
414
415 return $sql;
416 }
417
418 // --------------------------------------------------------------------
419
420 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000421 * Drop Table
422 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200423 * @param string $table_name Table name
424 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000425 * @return bool
426 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200427 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000428 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100429 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300430 {
431 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
432 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200433
434 $query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists);
435 if ($query === FALSE)
Andrey Andreevd947eba2012-04-09 14:58:28 +0300436 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200437 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300438 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200439 elseif ($query === TRUE)
440 {
441 return TRUE;
442 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300443
Andrey Andreeva287a342012-11-05 23:19:59 +0200444 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300445
446 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200447 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300448 {
449 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
450 if ($key !== FALSE)
451 {
452 unset($this->db->data_cache['table_names'][$key]);
453 }
454 }
455
Andrey Andreeva287a342012-11-05 23:19:59 +0200456 return $query;
457 }
458
459 // --------------------------------------------------------------------
460
461 /**
462 * Drop Table
463 *
464 * Generates a platform-specific DROP TABLE string
465 *
466 * @param string $table Table name
467 * @param bool $if_exists Whether to add an IF EXISTS condition
468 * @return string
469 */
470 protected function _drop_table($table, $if_exists)
471 {
472 $sql = 'DROP TABLE';
473
474 if ($if_exists)
475 {
476 if ($this->_drop_table_if === FALSE)
477 {
478 if ( ! $this->db->table_exists($table))
479 {
480 return TRUE;
481 }
482 }
483 else
484 {
485 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
486 }
487 }
488
489 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000490 }
491
492 // --------------------------------------------------------------------
493
494 /**
495 * Rename Table
496 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200497 * @param string $table_name Old table name
498 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000499 * @return bool
500 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000501 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000502 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100503 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000504 {
505 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300506 return FALSE;
507 }
508 elseif ($this->_rename_table === FALSE)
509 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200510 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000511 }
Barry Mienydd671972010-10-04 16:33:58 +0200512
Andrey Andreev5d281762012-06-11 22:05:40 +0300513 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300514 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
515 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
516 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300517
518 if ($result && ! empty($this->db->data_cache['table_names']))
519 {
520 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
521 if ($key !== FALSE)
522 {
523 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
524 }
525 }
526
527 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 }
529
530 // --------------------------------------------------------------------
531
532 /**
533 * Column Add
534 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200535 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200536 * @param string $table Table name
537 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200538 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000539 * @return bool
540 */
Andrey Andreevb67277b2012-11-12 12:51:14 +0200541 public function add_column($table = '', $field = array(), $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000542 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100543 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000544 {
545 show_error('A table name is required for that operation.');
546 }
547
Andrey Andreeva287a342012-11-05 23:19:59 +0200548 // Work-around for literal column definitions
549 if ( ! is_array($field))
550 {
551 $field = array($field);
552 }
553
Andrey Andreev24276a32012-01-08 02:44:38 +0200554 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200555 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200556 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
557 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
558 {
559 $field[$k]['after'] = $_after;
560 }
561
Barry Mienydd671972010-10-04 16:33:58 +0200562 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200563 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000564
Andrey Andreeva287a342012-11-05 23:19:59 +0200565 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
566 $this->_reset();
567 if ($sqls === FALSE)
568 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200569 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200570 }
Barry Mienydd671972010-10-04 16:33:58 +0200571
Andrey Andreeva287a342012-11-05 23:19:59 +0200572 for ($i = 0, $c = count($sqls); $i < $c; $i++)
573 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200574 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000575 {
576 return FALSE;
577 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000578 }
Barry Mienydd671972010-10-04 16:33:58 +0200579
Robin Sowell8a54ef22009-03-04 14:49:53 +0000580 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000581 }
582
583 // --------------------------------------------------------------------
584
585 /**
586 * Column Drop
587 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200588 * @param string $table Table name
589 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000590 * @return bool
591 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000592 public function drop_column($table = '', $column_name = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000593 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100594 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000595 {
596 show_error('A table name is required for that operation.');
597 }
598
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100599 if ($column_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000600 {
601 show_error('A column name is required for that operation.');
602 }
603
Andrey Andreeva287a342012-11-05 23:19:59 +0200604 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
605 if ($sql === FALSE)
606 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200607 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200608 }
609
610 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000611 }
612
613 // --------------------------------------------------------------------
614
615 /**
616 * Column Modify
617 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200618 * @param string $table Table name
619 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000620 * @return bool
621 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000622 public function modify_column($table = '', $field = array())
Derek Allard2067d1a2008-11-13 22:59:24 +0000623 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100624 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000625 {
626 show_error('A table name is required for that operation.');
627 }
628
Andrey Andreeva287a342012-11-05 23:19:59 +0200629 // Work-around for literal column definitions
630 if ( ! is_array($field))
631 {
632 $field = array($field);
633 }
634
Andrey Andreev24276a32012-01-08 02:44:38 +0200635 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000636 {
637 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200638 }
Barry Mienydd671972010-10-04 16:33:58 +0200639
Andrey Andreeva287a342012-11-05 23:19:59 +0200640 if (count($this->fields) === 0)
641 {
642 show_error('Field information is required.');
643 }
Barry Mienydd671972010-10-04 16:33:58 +0200644
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200645 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200646 $this->_reset();
647 if ($sqls === FALSE)
648 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200649 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200650 }
651
652 for ($i = 0, $c = count($sqls); $i < $c; $i++)
653 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200654 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000655 {
656 return FALSE;
657 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000658 }
Barry Mienydd671972010-10-04 16:33:58 +0200659
Robin Sowell8a54ef22009-03-04 14:49:53 +0000660 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000661 }
662
663 // --------------------------------------------------------------------
664
665 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200666 * ALTER TABLE
667 *
668 * @param string $alter_type ALTER type
669 * @param string $table Table name
670 * @param mixed $field Column definition
671 * @return string|string[]
672 */
673 protected function _alter_table($alter_type, $table, $field)
674 {
675 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
676
677 // DROP has everything it needs now.
678 if ($alter_type === 'DROP')
679 {
680 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
681 }
682
683 $sqls = array();
684 for ($i = 0, $c = count($field), $sql .= $alter_type.' COLUMN '; $i < $c; $i++)
685 {
686 $sqls[] = $sql
687 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
688 }
689
690 return $sqls;
691 }
692
693 // --------------------------------------------------------------------
694
695 /**
696 * Process fields
697 *
698 * @param bool $create_table
699 * @return array
700 */
701 protected function _process_fields($create_table = FALSE)
702 {
703 $fields = array();
704
705 foreach ($this->fields as $key => $attributes)
706 {
707 if (is_int($key) && ! is_array($attributes))
708 {
709 $fields[] = array('_literal' => $attributes);
710 continue;
711 }
712
713 $attributes = array_change_key_case($attributes, CASE_UPPER);
714
715 if ($create_table === TRUE && empty($attributes['TYPE']))
716 {
717 continue;
718 }
719
720 if (isset($attributes['TYPE']))
721 {
722 $this->_attr_type($attributes);
723 $this->_attr_unsigned($attributes, $field);
724 }
725
726 $field = array(
727 'name' => $key,
728 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
729 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
730 'length' => '',
731 'unsigned' => '',
732 'null' => '',
733 'unique' => '',
734 'default' => '',
735 'auto_increment' => '',
736 '_literal' => FALSE
737 );
738
739 $this->_attr_default($attributes, $field);
740
741 if (isset($attributes['NULL']))
742 {
743 if ($attributes['NULL'] === TRUE)
744 {
745 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
746 }
747 elseif ($create_table === TRUE)
748 {
749 $field['null'] = ' NOT NULL';
750 }
751 }
752
753 $this->_attr_auto_increment($attributes, $field);
754 $this->_attr_unique($attributes, $field);
755
756 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
757 {
758 switch (strtoupper($attributes['TYPE']))
759 {
760 case 'ENUM':
761 case 'SET':
762 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
763 default:
764 $field['length'] = is_array($attributes['CONSTRAINT'])
Andrey Andreev356d4f42012-11-19 20:13:11 +0200765 ? "('".implode("','", $attributes['CONSTRAINT'])."')"
Andrey Andreeva287a342012-11-05 23:19:59 +0200766 : '('.$attributes['CONSTRAINT'].')';
767 break;
768 }
769 }
770
771 $fields[] = $field;
772 }
773
774 return $fields;
775 }
776
777 // --------------------------------------------------------------------
778
779 /**
780 * Process column
781 *
782 * @param array $field
783 * @return string
784 */
785 protected function _process_column($field)
786 {
787 return $this->db->escape_identifiers($field['name'])
788 .' '.$field['type'].$field['length']
789 .$field['unsigned']
790 .$field['default']
791 .$field['null']
792 .$field['auto_increment']
793 .$field['unique'];
794 }
795
796 // --------------------------------------------------------------------
797
798 /**
799 * Field attribute TYPE
800 *
801 * Performs a data type mapping between different databases.
802 *
803 * @param array &$attributes
804 * @return void
805 */
806 protected function _attr_type(&$attributes)
807 {
808 // Usually overriden by drivers
809 }
810
811 // --------------------------------------------------------------------
812
813 /**
814 * Field attribute UNSIGNED
815 *
816 * Depending on the _unsigned property value:
817 *
818 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
819 * - FALSE will always set $field['unsigned'] to ''
820 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
821 * if $attributes['TYPE'] is found in the array
822 * - array(TYPE => UTYPE) will change $field['type'],
823 * from TYPE to UTYPE in case of a match
824 *
825 * @param array &$attributes
826 * @param array &$field
827 * @return void
828 */
829 protected function _attr_unsigned(&$attributes, &$field)
830 {
831 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
832 {
833 return;
834 }
835
836 // Reset the attribute in order to avoid issues if we do type conversion
837 $attributes['UNSIGNED'] = FALSE;
838
839 if (is_array($this->_unsigned))
840 {
841 foreach (array_keys($this->_unsigned) as $key)
842 {
843 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
844 {
845 $field['unsigned'] = ' UNSIGNED';
846 return;
847 }
848 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
849 {
850 $field['type'] = $key;
851 return;
852 }
853 }
854
855 return;
856 }
857
858 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
859 }
860
861 // --------------------------------------------------------------------
862
863 /**
864 * Field attribute DEFAULT
865 *
866 * @param array &$attributes
867 * @param array &$field
868 * @return void
869 */
870 protected function _attr_default(&$attributes, &$field)
871 {
872 if ($this->_default === FALSE)
873 {
874 return;
875 }
876
877 if (array_key_exists('DEFAULT', $attributes))
878 {
879 if ($attributes['DEFAULT'] === NULL)
880 {
881 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
882
883 // Override the NULL attribute if that's our default
884 $attributes['NULL'] = NULL;
885 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
886 }
887 else
888 {
889 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
890 }
891 }
892 }
893
894 // --------------------------------------------------------------------
895
896 /**
897 * Field attribute UNIQUE
898 *
899 * @param array &$attributes
900 * @param array &$field
901 * @return void
902 */
903 protected function _attr_unique(&$attributes, &$field)
904 {
905 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
906 {
907 $field['unique'] = ' UNIQUE';
908 }
909 }
910
911 // --------------------------------------------------------------------
912
913 /**
914 * Field attribute AUTO_INCREMENT
915 *
916 * @param array &$attributes
917 * @param array &$field
918 * @return void
919 */
920 protected function _attr_auto_increment(&$attributes, &$field)
921 {
922 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
923 {
924 $field['auto_increment'] = ' AUTO_INCREMENT';
925 }
926 }
927
928 // --------------------------------------------------------------------
929
930 /**
931 * Process primary keys
932 *
933 * @param string $table Table name
934 * @return string
935 */
936 protected function _process_primary_keys($table)
937 {
938 $sql = '';
939
940 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
941 {
942 if ( ! isset($this->fields[$this->primary_keys[$i]]))
943 {
944 unset($this->primary_keys[$i]);
945 }
946 }
947
948 if (count($this->primary_keys) > 0)
949 {
950 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
951 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
952 }
953
954 return $sql;
955 }
956
957 // --------------------------------------------------------------------
958
959 /**
960 * Process indexes
961 *
962 * @param string $table
963 * @return string
964 */
Andrey Andreev35451022012-11-25 17:20:04 +0200965 protected function _process_indexes($table)
Andrey Andreeva287a342012-11-05 23:19:59 +0200966 {
967 $table = $this->db->escape_identifiers($table);
968 $sqls = array();
969
970 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
971 {
Andrey Andreev35451022012-11-25 17:20:04 +0200972 if (is_array($this->keys[$i]))
973 {
974 for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
975 {
976 if ( ! isset($this->fields[$this->keys[$i][$i2]]))
977 {
978 unset($this->keys[$i][$i2]);
979 continue;
980 }
981 }
982 }
983 elseif ( ! isset($this->fields[$this->keys[$i]]))
Andrey Andreeva287a342012-11-05 23:19:59 +0200984 {
985 unset($this->keys[$i]);
986 continue;
987 }
988
989 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
990
991 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers(implode('_', $this->keys[$i]))
992 .' ON '.$this->db->escape_identifiers($table)
993 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
994 }
995
996 return $sqls;
997 }
998
999 // --------------------------------------------------------------------
1000
1001 /**
Derek Allard2067d1a2008-11-13 22:59:24 +00001002 * Reset
1003 *
1004 * Resets table creation vars
1005 *
Derek Allard2067d1a2008-11-13 22:59:24 +00001006 * @return void
1007 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +00001008 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +00001009 {
Andrey Andreev24276a32012-01-08 02:44:38 +02001010 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +00001011 }
1012
1013}
1014
1015/* End of file DB_forge.php */
Timothy Warren215890b2012-03-20 09:38:16 -04001016/* Location: ./system/database/DB_forge.php */