blob: d8ecefe77e0892c4df184df0b467cd7bda045399 [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 */
43 public $db;
44
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 *
153 * @return void
154 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200155 public function __construct()
Derek Allard2067d1a2008-11-13 22:59:24 +0000156 {
157 // Assign the main database object to $this->db
158 $CI =& get_instance();
159 $this->db =& $CI->db;
Andrey Andreev24276a32012-01-08 02:44:38 +0200160 log_message('debug', 'Database Forge Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000161 }
162
163 // --------------------------------------------------------------------
164
165 /**
166 * Create database
167 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200168 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000169 * @return bool
170 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200171 public function create_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000172 {
Andrey Andreevd947eba2012-04-09 14:58:28 +0300173 if ($this->_create_database === FALSE)
174 {
175 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
176 }
177 elseif ( ! $this->db->query(sprintf($this->_create_database, $db_name, $this->db->char_set, $this->db->dbcollat)))
178 {
179 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
180 }
181
Andrey Andreev5d281762012-06-11 22:05:40 +0300182 if ( ! empty($this->db->data_cache['db_names']))
183 {
184 $this->db->data_cache['db_names'][] = $db_name;
185 }
186
Andrey Andreevd947eba2012-04-09 14:58:28 +0300187 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000188 }
189
190 // --------------------------------------------------------------------
191
192 /**
193 * Drop database
194 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200195 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000196 * @return bool
197 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200198 public function drop_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000199 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100200 if ($db_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300201 {
202 show_error('A table name is required for that operation.');
203 return FALSE;
204 }
205 elseif ($this->_drop_database === FALSE)
206 {
207 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
208 }
209 elseif ( ! $this->db->query(sprintf($this->_drop_database, $db_name)))
210 {
211 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
212 }
213
Andrey Andreev5d281762012-06-11 22:05:40 +0300214 if ( ! empty($this->db->data_cache['db_names']))
215 {
216 $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
217 if ($key !== FALSE)
218 {
219 unset($this->db->data_cache['db_names'][$key]);
220 }
221 }
222
Andrey Andreevd947eba2012-04-09 14:58:28 +0300223 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000224 }
225
226 // --------------------------------------------------------------------
227
228 /**
229 * Add Key
230 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200231 * @param string $key
232 * @param bool $primary
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000233 * @return object
Derek Allard2067d1a2008-11-13 22:59:24 +0000234 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000235 public function add_key($key = '', $primary = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000236 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200237 if (empty($key))
238 {
239 show_error('Key information is required for that operation.');
240 }
241
242 if (is_array($key))
Derek Allard2067d1a2008-11-13 22:59:24 +0000243 {
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500244 foreach ($key as $one)
Derek Allard2067d1a2008-11-13 22:59:24 +0000245 {
246 $this->add_key($one, $primary);
247 }
Barry Mienydd671972010-10-04 16:33:58 +0200248
Andrey Andreeva287a342012-11-05 23:19:59 +0200249 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000250 }
Barry Mienydd671972010-10-04 16:33:58 +0200251
Derek Allard2067d1a2008-11-13 22:59:24 +0000252 if ($primary === TRUE)
253 {
254 $this->primary_keys[] = $key;
255 }
256 else
257 {
258 $this->keys[] = $key;
259 }
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000260
261 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000262 }
263
264 // --------------------------------------------------------------------
265
266 /**
267 * Add Field
268 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200269 * @param array $field
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000270 * @return object
Derek Allard2067d1a2008-11-13 22:59:24 +0000271 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000272 public function add_field($field = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000273 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200274 if (empty($field))
Derek Allard2067d1a2008-11-13 22:59:24 +0000275 {
276 show_error('Field information is required.');
277 }
Barry Mienydd671972010-10-04 16:33:58 +0200278
Derek Allard2067d1a2008-11-13 22:59:24 +0000279 if (is_string($field))
280 {
Andrey Andreev24276a32012-01-08 02:44:38 +0200281 if ($field === 'id')
Derek Allard2067d1a2008-11-13 22:59:24 +0000282 {
283 $this->add_field(array(
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000284 'id' => array(
285 'type' => 'INT',
286 'constraint' => 9,
287 'auto_increment' => TRUE
288 )
289 ));
Derek Allard2067d1a2008-11-13 22:59:24 +0000290 $this->add_key('id', TRUE);
291 }
292 else
293 {
294 if (strpos($field, ' ') === FALSE)
295 {
296 show_error('Field information is required for that operation.');
297 }
Barry Mienydd671972010-10-04 16:33:58 +0200298
Derek Allard2067d1a2008-11-13 22:59:24 +0000299 $this->fields[] = $field;
300 }
301 }
Barry Mienydd671972010-10-04 16:33:58 +0200302
Derek Allard2067d1a2008-11-13 22:59:24 +0000303 if (is_array($field))
304 {
305 $this->fields = array_merge($this->fields, $field);
306 }
Andrey Andreev24276a32012-01-08 02:44:38 +0200307
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000308 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000309 }
310
311 // --------------------------------------------------------------------
312
313 /**
314 * Create Table
315 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200316 * @param string $table Table name
317 * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000318 * @return bool
319 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000320 public function create_table($table = '', $if_not_exists = FALSE)
Barry Mienydd671972010-10-04 16:33:58 +0200321 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100322 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000323 {
324 show_error('A table name is required for that operation.');
325 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200326 else
327 {
328 $table = $this->db->dbprefix.$table;
329 }
Barry Mienydd671972010-10-04 16:33:58 +0200330
Andrey Andreev24276a32012-01-08 02:44:38 +0200331 if (count($this->fields) === 0)
Barry Mienydd671972010-10-04 16:33:58 +0200332 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000333 show_error('Field information is required.');
334 }
335
Andrey Andreeva287a342012-11-05 23:19:59 +0200336 $sql = $this->_create_table($table, $if_not_exists);
Andrey Andreev5d281762012-06-11 22:05:40 +0300337
338 if (is_bool($sql))
339 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200340 $this->_reset();
341 if ($sql === FALSE)
342 {
343 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
344 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300345 }
346
Andrey Andreeva287a342012-11-05 23:19:59 +0200347 if (($result = $this->db->query($sql)) !== FALSE)
Andrey Andreev5d281762012-06-11 22:05:40 +0300348 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200349 empty($this->db->data_cache['table_names']) OR $this->db->data_cache['table_names'][] = $table;
350
351 // Most databases don't support creating indexes from within the CREATE TABLE statement
352 if ( ! empty($this->keys))
353 {
354 for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
355 {
356 $this->db->query($sqls[$i]);
357 }
358 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300359 }
360
Andrey Andreeva287a342012-11-05 23:19:59 +0200361 $this->_reset();
Andrey Andreev5d281762012-06-11 22:05:40 +0300362 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000363 }
364
365 // --------------------------------------------------------------------
366
367 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200368 * Create Table
369 *
370 * @param string $table Table name
371 * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
372 * @return mixed
373 */
374 protected function _create_table($table, $if_not_exists)
375 {
376 if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
377 {
378 if ($this->db->table_exists($table))
379 {
380 return TRUE;
381 }
382 else
383 {
384 $if_not_exists = FALSE;
385 }
386 }
387
388 $sql = ($if_not_exists)
389 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
390 : 'CREATE TABLE';
391
392 $columns = $this->_process_fields(TRUE);
393 for ($i = 0, $c = count($columns); $i < $c; $i++)
394 {
395 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
396 ? "\n\t".$columns[$i]['_literal']
397 : "\n\t".$this->_process_column($columns[$i]);
398 }
399
400 $columns = implode(',', $columns)
401 .$this->_process_primary_keys($table);
402
403 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
404 if ($this->_create_table_keys === TRUE)
405 {
406 $columns .= $this->_process_indexes();
407 }
408
409 // _create_table will usually have the following format: "%s %s (%s\n)"
410 $sql = sprintf($this->_create_table.';',
411 $sql,
412 $this->db->escape_identifiers($table),
413 $columns
414 );
415
416 return $sql;
417 }
418
419 // --------------------------------------------------------------------
420
421 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000422 * Drop Table
423 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200424 * @param string $table_name Table name
425 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000426 * @return bool
427 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200428 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000429 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100430 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300431 {
432 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
433 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200434
435 $query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists);
436 if ($query === FALSE)
Andrey Andreevd947eba2012-04-09 14:58:28 +0300437 {
438 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
439 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200440 elseif ($query === TRUE)
441 {
442 return TRUE;
443 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300444
Andrey Andreeva287a342012-11-05 23:19:59 +0200445 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300446
447 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200448 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300449 {
450 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
451 if ($key !== FALSE)
452 {
453 unset($this->db->data_cache['table_names'][$key]);
454 }
455 }
456
Andrey Andreeva287a342012-11-05 23:19:59 +0200457 return $query;
458 }
459
460 // --------------------------------------------------------------------
461
462 /**
463 * Drop Table
464 *
465 * Generates a platform-specific DROP TABLE string
466 *
467 * @param string $table Table name
468 * @param bool $if_exists Whether to add an IF EXISTS condition
469 * @return string
470 */
471 protected function _drop_table($table, $if_exists)
472 {
473 $sql = 'DROP TABLE';
474
475 if ($if_exists)
476 {
477 if ($this->_drop_table_if === FALSE)
478 {
479 if ( ! $this->db->table_exists($table))
480 {
481 return TRUE;
482 }
483 }
484 else
485 {
486 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
487 }
488 }
489
490 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000491 }
492
493 // --------------------------------------------------------------------
494
495 /**
496 * Rename Table
497 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200498 * @param string $table_name Old table name
499 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000500 * @return bool
501 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000502 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000503 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100504 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000505 {
506 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300507 return FALSE;
508 }
509 elseif ($this->_rename_table === FALSE)
510 {
511 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000512 }
Barry Mienydd671972010-10-04 16:33:58 +0200513
Andrey Andreev5d281762012-06-11 22:05:40 +0300514 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300515 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
516 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
517 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300518
519 if ($result && ! empty($this->db->data_cache['table_names']))
520 {
521 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
522 if ($key !== FALSE)
523 {
524 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
525 }
526 }
527
528 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000529 }
530
531 // --------------------------------------------------------------------
532
533 /**
534 * Column Add
535 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200536 * @param string $table Table name
537 * @param array $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000538 * @return bool
539 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200540 public function add_column($table = '', $field = array())
Derek Allard2067d1a2008-11-13 22:59:24 +0000541 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100542 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000543 {
544 show_error('A table name is required for that operation.');
545 }
546
Andrey Andreeva287a342012-11-05 23:19:59 +0200547 // Work-around for literal column definitions
548 if ( ! is_array($field))
549 {
550 $field = array($field);
551 }
552
Andrey Andreev24276a32012-01-08 02:44:38 +0200553 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200554 {
555 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200556 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000557
Andrey Andreeva287a342012-11-05 23:19:59 +0200558 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
559 $this->_reset();
560 if ($sqls === FALSE)
561 {
562 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
563 }
Barry Mienydd671972010-10-04 16:33:58 +0200564
Andrey Andreeva287a342012-11-05 23:19:59 +0200565 for ($i = 0, $c = count($sqls); $i < $c; $i++)
566 {
Robin Sowell8a54ef22009-03-04 14:49:53 +0000567 if ($this->db->query($sql) === FALSE)
568 {
569 return FALSE;
570 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000571 }
Barry Mienydd671972010-10-04 16:33:58 +0200572
Robin Sowell8a54ef22009-03-04 14:49:53 +0000573 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000574 }
575
576 // --------------------------------------------------------------------
577
578 /**
579 * Column Drop
580 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200581 * @param string $table Table name
582 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000583 * @return bool
584 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000585 public function drop_column($table = '', $column_name = '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000586 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100587 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000588 {
589 show_error('A table name is required for that operation.');
590 }
591
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100592 if ($column_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000593 {
594 show_error('A column name is required for that operation.');
595 }
596
Andrey Andreeva287a342012-11-05 23:19:59 +0200597 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
598 if ($sql === FALSE)
599 {
600 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
601 }
602
603 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000604 }
605
606 // --------------------------------------------------------------------
607
608 /**
609 * Column Modify
610 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200611 * @param string $table Table name
612 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000613 * @return bool
614 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000615 public function modify_column($table = '', $field = array())
Derek Allard2067d1a2008-11-13 22:59:24 +0000616 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100617 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000618 {
619 show_error('A table name is required for that operation.');
620 }
621
Andrey Andreeva287a342012-11-05 23:19:59 +0200622 // Work-around for literal column definitions
623 if ( ! is_array($field))
624 {
625 $field = array($field);
626 }
627
Andrey Andreev24276a32012-01-08 02:44:38 +0200628 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000629 {
630 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200631 }
Barry Mienydd671972010-10-04 16:33:58 +0200632
Andrey Andreeva287a342012-11-05 23:19:59 +0200633 if (count($this->fields) === 0)
634 {
635 show_error('Field information is required.');
636 }
Barry Mienydd671972010-10-04 16:33:58 +0200637
Andrey Andreeva287a342012-11-05 23:19:59 +0200638 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->fields);
639 $this->_reset();
640 if ($sqls === FALSE)
641 {
642 return ($this->db->db_debug) ? $this->db->display_error('db_unsuported_feature') : FALSE;
643 }
644
645 for ($i = 0, $c = count($sqls); $i < $c; $i++)
646 {
Robin Sowell8a54ef22009-03-04 14:49:53 +0000647 if ($this->db->query($sql) === FALSE)
648 {
649 return FALSE;
650 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000651 }
Barry Mienydd671972010-10-04 16:33:58 +0200652
Robin Sowell8a54ef22009-03-04 14:49:53 +0000653 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000654 }
655
656 // --------------------------------------------------------------------
657
658 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200659 * ALTER TABLE
660 *
661 * @param string $alter_type ALTER type
662 * @param string $table Table name
663 * @param mixed $field Column definition
664 * @return string|string[]
665 */
666 protected function _alter_table($alter_type, $table, $field)
667 {
668 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
669
670 // DROP has everything it needs now.
671 if ($alter_type === 'DROP')
672 {
673 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
674 }
675
676 $sqls = array();
677 for ($i = 0, $c = count($field), $sql .= $alter_type.' COLUMN '; $i < $c; $i++)
678 {
679 $sqls[] = $sql
680 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
681 }
682
683 return $sqls;
684 }
685
686 // --------------------------------------------------------------------
687
688 /**
689 * Process fields
690 *
691 * @param bool $create_table
692 * @return array
693 */
694 protected function _process_fields($create_table = FALSE)
695 {
696 $fields = array();
697
698 foreach ($this->fields as $key => $attributes)
699 {
700 if (is_int($key) && ! is_array($attributes))
701 {
702 $fields[] = array('_literal' => $attributes);
703 continue;
704 }
705
706 $attributes = array_change_key_case($attributes, CASE_UPPER);
707
708 if ($create_table === TRUE && empty($attributes['TYPE']))
709 {
710 continue;
711 }
712
713 if (isset($attributes['TYPE']))
714 {
715 $this->_attr_type($attributes);
716 $this->_attr_unsigned($attributes, $field);
717 }
718
719 $field = array(
720 'name' => $key,
721 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
722 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
723 'length' => '',
724 'unsigned' => '',
725 'null' => '',
726 'unique' => '',
727 'default' => '',
728 'auto_increment' => '',
729 '_literal' => FALSE
730 );
731
732 $this->_attr_default($attributes, $field);
733
734 if (isset($attributes['NULL']))
735 {
736 if ($attributes['NULL'] === TRUE)
737 {
738 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
739 }
740 elseif ($create_table === TRUE)
741 {
742 $field['null'] = ' NOT NULL';
743 }
744 }
745
746 $this->_attr_auto_increment($attributes, $field);
747 $this->_attr_unique($attributes, $field);
748
749 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
750 {
751 switch (strtoupper($attributes['TYPE']))
752 {
753 case 'ENUM':
754 case 'SET':
755 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
756 default:
757 $field['length'] = is_array($attributes['CONSTRAINT'])
758 ? '('.implode(',', $attributes['CONSTRAINT']).')'
759 : '('.$attributes['CONSTRAINT'].')';
760 break;
761 }
762 }
763
764 $fields[] = $field;
765 }
766
767 return $fields;
768 }
769
770 // --------------------------------------------------------------------
771
772 /**
773 * Process column
774 *
775 * @param array $field
776 * @return string
777 */
778 protected function _process_column($field)
779 {
780 return $this->db->escape_identifiers($field['name'])
781 .' '.$field['type'].$field['length']
782 .$field['unsigned']
783 .$field['default']
784 .$field['null']
785 .$field['auto_increment']
786 .$field['unique'];
787 }
788
789 // --------------------------------------------------------------------
790
791 /**
792 * Field attribute TYPE
793 *
794 * Performs a data type mapping between different databases.
795 *
796 * @param array &$attributes
797 * @return void
798 */
799 protected function _attr_type(&$attributes)
800 {
801 // Usually overriden by drivers
802 }
803
804 // --------------------------------------------------------------------
805
806 /**
807 * Field attribute UNSIGNED
808 *
809 * Depending on the _unsigned property value:
810 *
811 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
812 * - FALSE will always set $field['unsigned'] to ''
813 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
814 * if $attributes['TYPE'] is found in the array
815 * - array(TYPE => UTYPE) will change $field['type'],
816 * from TYPE to UTYPE in case of a match
817 *
818 * @param array &$attributes
819 * @param array &$field
820 * @return void
821 */
822 protected function _attr_unsigned(&$attributes, &$field)
823 {
824 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
825 {
826 return;
827 }
828
829 // Reset the attribute in order to avoid issues if we do type conversion
830 $attributes['UNSIGNED'] = FALSE;
831
832 if (is_array($this->_unsigned))
833 {
834 foreach (array_keys($this->_unsigned) as $key)
835 {
836 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
837 {
838 $field['unsigned'] = ' UNSIGNED';
839 return;
840 }
841 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
842 {
843 $field['type'] = $key;
844 return;
845 }
846 }
847
848 return;
849 }
850
851 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
852 }
853
854 // --------------------------------------------------------------------
855
856 /**
857 * Field attribute DEFAULT
858 *
859 * @param array &$attributes
860 * @param array &$field
861 * @return void
862 */
863 protected function _attr_default(&$attributes, &$field)
864 {
865 if ($this->_default === FALSE)
866 {
867 return;
868 }
869
870 if (array_key_exists('DEFAULT', $attributes))
871 {
872 if ($attributes['DEFAULT'] === NULL)
873 {
874 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
875
876 // Override the NULL attribute if that's our default
877 $attributes['NULL'] = NULL;
878 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
879 }
880 else
881 {
882 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
883 }
884 }
885 }
886
887 // --------------------------------------------------------------------
888
889 /**
890 * Field attribute UNIQUE
891 *
892 * @param array &$attributes
893 * @param array &$field
894 * @return void
895 */
896 protected function _attr_unique(&$attributes, &$field)
897 {
898 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
899 {
900 $field['unique'] = ' UNIQUE';
901 }
902 }
903
904 // --------------------------------------------------------------------
905
906 /**
907 * Field attribute AUTO_INCREMENT
908 *
909 * @param array &$attributes
910 * @param array &$field
911 * @return void
912 */
913 protected function _attr_auto_increment(&$attributes, &$field)
914 {
915 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
916 {
917 $field['auto_increment'] = ' AUTO_INCREMENT';
918 }
919 }
920
921 // --------------------------------------------------------------------
922
923 /**
924 * Process primary keys
925 *
926 * @param string $table Table name
927 * @return string
928 */
929 protected function _process_primary_keys($table)
930 {
931 $sql = '';
932
933 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
934 {
935 if ( ! isset($this->fields[$this->primary_keys[$i]]))
936 {
937 unset($this->primary_keys[$i]);
938 }
939 }
940
941 if (count($this->primary_keys) > 0)
942 {
943 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
944 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
945 }
946
947 return $sql;
948 }
949
950 // --------------------------------------------------------------------
951
952 /**
953 * Process indexes
954 *
955 * @param string $table
956 * @return string
957 */
958 protected function _process_indexes($table = NULL)
959 {
960 $table = $this->db->escape_identifiers($table);
961 $sqls = array();
962
963 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
964 {
965 if ( ! isset($this->fields[$this->keys[$i]]))
966 {
967 unset($this->keys[$i]);
968 continue;
969 }
970
971 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
972
973 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers(implode('_', $this->keys[$i]))
974 .' ON '.$this->db->escape_identifiers($table)
975 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
976 }
977
978 return $sqls;
979 }
980
981 // --------------------------------------------------------------------
982
983 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000984 * Reset
985 *
986 * Resets table creation vars
987 *
Derek Allard2067d1a2008-11-13 22:59:24 +0000988 * @return void
989 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000990 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +0000991 {
Andrey Andreev24276a32012-01-08 02:44:38 +0200992 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +0000993 }
994
995}
996
997/* End of file DB_forge.php */
Timothy Warren215890b2012-03-20 09:38:16 -0400998/* Location: ./system/database/DB_forge.php */