blob: 4238e37ee39b3c030375eac8122fa8da7327f0ef [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 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02007 * This content is released under the MIT License (MIT)
Andrey Andreev24276a32012-01-08 02:44:38 +02008 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +02009 * Copyright (c) 2014, British Columbia Institute of Technology
Andrey Andreev24276a32012-01-08 02:44:38 +020010 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020011 * 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:
Derek Jonesf4a4bd82011-10-20 12:18:42 -050017 *
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020018 * 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
darwinel871754a2014-02-11 17:34:57 +010031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (http://ellislab.com/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020032 * @copyright Copyright (c) 2014, British Columbia Institute of Technology (http://bcit.ca/)
33 * @license http://opensource.org/licenses/MIT MIT License
34 * @link http://codeigniter.com
35 * @since Version 1.0.0
Derek Allard2067d1a2008-11-13 22:59:24 +000036 * @filesource
37 */
Andrey Andreevc5536aa2012-11-01 17:33:58 +020038defined('BASEPATH') OR exit('No direct script access allowed');
Derek Allard2067d1a2008-11-13 22:59:24 +000039
Derek Allard2067d1a2008-11-13 22:59:24 +000040/**
Andrey Andreevd947eba2012-04-09 14:58:28 +030041 * Database Forge Class
Derek Allard2067d1a2008-11-13 22:59:24 +000042 *
43 * @category Database
Derek Jonesf4a4bd82011-10-20 12:18:42 -050044 * @author EllisLab Dev Team
Derek Allard2067d1a2008-11-13 22:59:24 +000045 * @link http://codeigniter.com/user_guide/database/
46 */
Timothy Warren833d5042012-03-19 16:12:03 -040047abstract class CI_DB_forge {
Derek Allard2067d1a2008-11-13 22:59:24 +000048
Andrey Andreevae85eb42012-11-02 01:42:31 +020049 /**
Andrey Andreeva287a342012-11-05 23:19:59 +020050 * Database object
51 *
52 * @var object
53 */
Andrey Andreeveaa60c72012-11-06 01:11:22 +020054 protected $db;
Andrey Andreeva287a342012-11-05 23:19:59 +020055
56 /**
Andrey Andreevae85eb42012-11-02 01:42:31 +020057 * Fields data
58 *
59 * @var array
60 */
Andrey Andreev24276a32012-01-08 02:44:38 +020061 public $fields = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020062
63 /**
64 * Keys data
65 *
66 * @var array
67 */
Andrey Andreev24276a32012-01-08 02:44:38 +020068 public $keys = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020069
70 /**
71 * Primary Keys data
72 *
73 * @var array
74 */
Andrey Andreev24276a32012-01-08 02:44:38 +020075 public $primary_keys = array();
Andrey Andreevae85eb42012-11-02 01:42:31 +020076
77 /**
78 * Database character set
79 *
80 * @var string
81 */
Andrey Andreev5fd3ae82012-10-24 14:55:35 +030082 public $db_char_set = '';
Derek Allard2067d1a2008-11-13 22:59:24 +000083
Andrey Andreevae85eb42012-11-02 01:42:31 +020084 // --------------------------------------------------------------------
85
86 /**
87 * CREATE DATABASE statement
88 *
89 * @var string
90 */
Andrey Andreevd947eba2012-04-09 14:58:28 +030091 protected $_create_database = 'CREATE DATABASE %s';
Andrey Andreevae85eb42012-11-02 01:42:31 +020092
93 /**
94 * DROP DATABASE statement
95 *
96 * @var string
97 */
Andrey Andreevd947eba2012-04-09 14:58:28 +030098 protected $_drop_database = 'DROP DATABASE %s';
Andrey Andreevae85eb42012-11-02 01:42:31 +020099
100 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200101 * CREATE TABLE statement
Andrey Andreevae85eb42012-11-02 01:42:31 +0200102 *
103 * @var string
104 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200105 protected $_create_table = "%s %s (%s\n)";
106
107 /**
108 * CREATE TABLE IF statement
109 *
110 * @var string
111 */
112 protected $_create_table_if = 'CREATE TABLE IF NOT EXISTS';
113
114 /**
115 * CREATE TABLE keys flag
116 *
117 * Whether table keys are created from within the
118 * CREATE TABLE statement.
119 *
120 * @var bool
121 */
122 protected $_create_table_keys = FALSE;
123
124 /**
125 * DROP TABLE IF EXISTS statement
126 *
127 * @var string
128 */
129 protected $_drop_table_if = 'DROP TABLE IF EXISTS';
Andrey Andreevae85eb42012-11-02 01:42:31 +0200130
131 /**
132 * RENAME TABLE statement
133 *
134 * @var string
135 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200136 protected $_rename_table = 'ALTER TABLE %s RENAME TO %s;';
137
138 /**
139 * UNSIGNED support
140 *
141 * @var bool|array
142 */
143 protected $_unsigned = TRUE;
144
145 /**
146 * NULL value representatin in CREATE/ALTER TABLE statements
147 *
148 * @var string
149 */
150 protected $_null = '';
151
152 /**
153 * DEFAULT value representation in CREATE/ALTER TABLE statements
154 *
155 * @var string
156 */
157 protected $_default = ' DEFAULT ';
Andrey Andreevd947eba2012-04-09 14:58:28 +0300158
Andrey Andreevae85eb42012-11-02 01:42:31 +0200159 // --------------------------------------------------------------------
160
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300161 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200162 * Class constructor
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300163 *
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200164 * @param object &$db Database object
Andrey Andreev5fd3ae82012-10-24 14:55:35 +0300165 * @return void
166 */
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200167 public function __construct(&$db)
Derek Allard2067d1a2008-11-13 22:59:24 +0000168 {
Andrey Andreeveaa60c72012-11-06 01:11:22 +0200169 $this->db =& $db;
Andrey Andreev24276a32012-01-08 02:44:38 +0200170 log_message('debug', 'Database Forge Class Initialized');
Derek Allard2067d1a2008-11-13 22:59:24 +0000171 }
172
173 // --------------------------------------------------------------------
174
175 /**
176 * Create database
177 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200178 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000179 * @return bool
180 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200181 public function create_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000182 {
Andrey Andreevd947eba2012-04-09 14:58:28 +0300183 if ($this->_create_database === FALSE)
184 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200185 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300186 }
187 elseif ( ! $this->db->query(sprintf($this->_create_database, $db_name, $this->db->char_set, $this->db->dbcollat)))
188 {
189 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
190 }
191
Andrey Andreev5d281762012-06-11 22:05:40 +0300192 if ( ! empty($this->db->data_cache['db_names']))
193 {
194 $this->db->data_cache['db_names'][] = $db_name;
195 }
196
Andrey Andreevd947eba2012-04-09 14:58:28 +0300197 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000198 }
199
200 // --------------------------------------------------------------------
201
202 /**
203 * Drop database
204 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200205 * @param string $db_name
Derek Allard2067d1a2008-11-13 22:59:24 +0000206 * @return bool
207 */
Andrey Andreev24276a32012-01-08 02:44:38 +0200208 public function drop_database($db_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000209 {
Andrey Andreevb9061492014-12-04 16:33:24 +0200210 if ($this->_drop_database === FALSE)
Andrey Andreevd947eba2012-04-09 14:58:28 +0300211 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200212 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300213 }
214 elseif ( ! $this->db->query(sprintf($this->_drop_database, $db_name)))
215 {
216 return ($this->db->db_debug) ? $this->db->display_error('db_unable_to_drop') : FALSE;
217 }
218
Andrey Andreev5d281762012-06-11 22:05:40 +0300219 if ( ! empty($this->db->data_cache['db_names']))
220 {
221 $key = array_search(strtolower($db_name), array_map('strtolower', $this->db->data_cache['db_names']), TRUE);
222 if ($key !== FALSE)
223 {
224 unset($this->db->data_cache['db_names'][$key]);
225 }
226 }
227
Andrey Andreevd947eba2012-04-09 14:58:28 +0300228 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000229 }
230
231 // --------------------------------------------------------------------
232
233 /**
234 * Add Key
235 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200236 * @param string $key
237 * @param bool $primary
Andrey Andreevd8dba5d2012-12-17 15:42:01 +0200238 * @return CI_DB_forge
Derek Allard2067d1a2008-11-13 22:59:24 +0000239 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200240 public function add_key($key, $primary = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000241 {
Andrey Andreevd743cdb2012-11-06 00:00:01 +0200242 if ($primary === TRUE && 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
Andrew Podner4296a652012-12-17 07:51:15 -0500270 * @return CI_DB_forge
Derek Allard2067d1a2008-11-13 22:59:24 +0000271 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200272 public function add_field($field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000273 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000274 if (is_string($field))
275 {
Andrey Andreev24276a32012-01-08 02:44:38 +0200276 if ($field === 'id')
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 {
278 $this->add_field(array(
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000279 'id' => array(
280 'type' => 'INT',
281 'constraint' => 9,
282 'auto_increment' => TRUE
283 )
284 ));
Derek Allard2067d1a2008-11-13 22:59:24 +0000285 $this->add_key('id', TRUE);
286 }
287 else
288 {
289 if (strpos($field, ' ') === FALSE)
290 {
291 show_error('Field information is required for that operation.');
292 }
Barry Mienydd671972010-10-04 16:33:58 +0200293
Derek Allard2067d1a2008-11-13 22:59:24 +0000294 $this->fields[] = $field;
295 }
296 }
Barry Mienydd671972010-10-04 16:33:58 +0200297
Derek Allard2067d1a2008-11-13 22:59:24 +0000298 if (is_array($field))
299 {
300 $this->fields = array_merge($this->fields, $field);
301 }
Andrey Andreev24276a32012-01-08 02:44:38 +0200302
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000303 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000304 }
305
306 // --------------------------------------------------------------------
307
308 /**
309 * Create Table
310 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200311 * @param string $table Table name
312 * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
Andrey Andreev27f798b2014-01-20 18:19:13 +0200313 * @param array $attributes Associative array of table attributes
Derek Allard2067d1a2008-11-13 22:59:24 +0000314 * @return bool
315 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200316 public function create_table($table, $if_not_exists = FALSE, array $attributes = array())
Barry Mienydd671972010-10-04 16:33:58 +0200317 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100318 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000319 {
320 show_error('A table name is required for that operation.');
321 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200322 else
323 {
324 $table = $this->db->dbprefix.$table;
325 }
Barry Mienydd671972010-10-04 16:33:58 +0200326
Andrey Andreev24276a32012-01-08 02:44:38 +0200327 if (count($this->fields) === 0)
Barry Mienydd671972010-10-04 16:33:58 +0200328 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000329 show_error('Field information is required.');
330 }
331
Andrey Andreev27f798b2014-01-20 18:19:13 +0200332 $sql = $this->_create_table($table, $if_not_exists, $attributes);
Andrey Andreev5d281762012-06-11 22:05:40 +0300333
334 if (is_bool($sql))
335 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200336 $this->_reset();
337 if ($sql === FALSE)
338 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200339 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200340 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300341 }
342
Andrey Andreeva287a342012-11-05 23:19:59 +0200343 if (($result = $this->db->query($sql)) !== FALSE)
Andrey Andreev5d281762012-06-11 22:05:40 +0300344 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200345 empty($this->db->data_cache['table_names']) OR $this->db->data_cache['table_names'][] = $table;
346
347 // Most databases don't support creating indexes from within the CREATE TABLE statement
348 if ( ! empty($this->keys))
349 {
350 for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
351 {
352 $this->db->query($sqls[$i]);
353 }
354 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300355 }
356
Andrey Andreeva287a342012-11-05 23:19:59 +0200357 $this->_reset();
Andrey Andreev5d281762012-06-11 22:05:40 +0300358 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000359 }
360
361 // --------------------------------------------------------------------
362
363 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200364 * Create Table
365 *
366 * @param string $table Table name
367 * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
Andrey Andreev27f798b2014-01-20 18:19:13 +0200368 * @param array $attributes Associative array of table attributes
Andrey Andreeva287a342012-11-05 23:19:59 +0200369 * @return mixed
370 */
Andrey Andreev27f798b2014-01-20 18:19:13 +0200371 protected function _create_table($table, $if_not_exists, $attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200372 {
373 if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
374 {
375 if ($this->db->table_exists($table))
376 {
377 return TRUE;
378 }
379 else
380 {
381 $if_not_exists = FALSE;
382 }
383 }
384
385 $sql = ($if_not_exists)
386 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
387 : 'CREATE TABLE';
388
389 $columns = $this->_process_fields(TRUE);
390 for ($i = 0, $c = count($columns); $i < $c; $i++)
391 {
392 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
393 ? "\n\t".$columns[$i]['_literal']
394 : "\n\t".$this->_process_column($columns[$i]);
395 }
396
397 $columns = implode(',', $columns)
398 .$this->_process_primary_keys($table);
399
400 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
401 if ($this->_create_table_keys === TRUE)
402 {
Andrey Andreev35451022012-11-25 17:20:04 +0200403 $columns .= $this->_process_indexes($table);
Andrey Andreeva287a342012-11-05 23:19:59 +0200404 }
405
406 // _create_table will usually have the following format: "%s %s (%s\n)"
Andrey Andreevaaca5cb2014-03-31 17:20:55 +0300407 $sql = sprintf($this->_create_table.'%s',
Andrey Andreeva287a342012-11-05 23:19:59 +0200408 $sql,
409 $this->db->escape_identifiers($table),
Andrey Andreev27f798b2014-01-20 18:19:13 +0200410 $columns,
411 $this->_create_table_attr($attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200412 );
413
414 return $sql;
415 }
416
417 // --------------------------------------------------------------------
418
419 /**
Andrey Andreev27f798b2014-01-20 18:19:13 +0200420 * CREATE TABLE attributes
421 *
422 * @param array $attributes Associative array of table attributes
423 * @return string
424 */
425 protected function _create_table_attr($attributes)
426 {
427 $sql = '';
428
429 foreach (array_keys($attributes) as $key)
430 {
431 if (is_string($key))
432 {
433 $sql .= ' '.strtoupper($key).' '.$attributes[$key];
434 }
435 }
436
437 return $sql;
438 }
439
440 // --------------------------------------------------------------------
441
442 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000443 * Drop Table
444 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200445 * @param string $table_name Table name
446 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000447 * @return bool
448 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200449 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000450 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100451 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300452 {
453 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
454 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200455
456 $query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists);
457 if ($query === FALSE)
Andrey Andreevd947eba2012-04-09 14:58:28 +0300458 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200459 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreevd947eba2012-04-09 14:58:28 +0300460 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200461 elseif ($query === TRUE)
462 {
463 return TRUE;
464 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300465
Andrey Andreeva287a342012-11-05 23:19:59 +0200466 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300467
468 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200469 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300470 {
471 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
472 if ($key !== FALSE)
473 {
474 unset($this->db->data_cache['table_names'][$key]);
475 }
476 }
477
Andrey Andreeva287a342012-11-05 23:19:59 +0200478 return $query;
479 }
480
481 // --------------------------------------------------------------------
482
483 /**
484 * Drop Table
485 *
486 * Generates a platform-specific DROP TABLE string
487 *
488 * @param string $table Table name
489 * @param bool $if_exists Whether to add an IF EXISTS condition
490 * @return string
491 */
492 protected function _drop_table($table, $if_exists)
493 {
494 $sql = 'DROP TABLE';
495
496 if ($if_exists)
497 {
498 if ($this->_drop_table_if === FALSE)
499 {
500 if ( ! $this->db->table_exists($table))
501 {
502 return TRUE;
503 }
504 }
505 else
506 {
507 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
508 }
509 }
510
511 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000512 }
513
514 // --------------------------------------------------------------------
515
516 /**
517 * Rename Table
518 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200519 * @param string $table_name Old table name
520 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000521 * @return bool
522 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000523 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000524 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100525 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 {
527 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300528 return FALSE;
529 }
530 elseif ($this->_rename_table === FALSE)
531 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200532 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000533 }
Barry Mienydd671972010-10-04 16:33:58 +0200534
Andrey Andreev5d281762012-06-11 22:05:40 +0300535 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300536 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
537 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
538 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300539
540 if ($result && ! empty($this->db->data_cache['table_names']))
541 {
542 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
543 if ($key !== FALSE)
544 {
545 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
546 }
547 }
548
549 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000550 }
551
552 // --------------------------------------------------------------------
553
554 /**
555 * Column Add
556 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200557 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200558 * @param string $table Table name
559 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200560 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000561 * @return bool
562 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200563 public function add_column($table, $field, $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000564 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200565 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200566 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200567
Andrey Andreev24276a32012-01-08 02:44:38 +0200568 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200569 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200570 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
571 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
572 {
573 $field[$k]['after'] = $_after;
574 }
575
Barry Mienydd671972010-10-04 16:33:58 +0200576 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200577 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000578
Andrey Andreeva287a342012-11-05 23:19:59 +0200579 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
580 $this->_reset();
581 if ($sqls === FALSE)
582 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200583 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200584 }
Barry Mienydd671972010-10-04 16:33:58 +0200585
Andrey Andreeva287a342012-11-05 23:19:59 +0200586 for ($i = 0, $c = count($sqls); $i < $c; $i++)
587 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200588 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000589 {
590 return FALSE;
591 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000592 }
Barry Mienydd671972010-10-04 16:33:58 +0200593
Robin Sowell8a54ef22009-03-04 14:49:53 +0000594 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000595 }
596
597 // --------------------------------------------------------------------
598
599 /**
600 * Column Drop
601 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200602 * @param string $table Table name
603 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000604 * @return bool
605 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200606 public function drop_column($table, $column_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000607 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200608 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
609 if ($sql === FALSE)
610 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200611 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200612 }
613
614 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000615 }
616
617 // --------------------------------------------------------------------
618
619 /**
620 * Column Modify
621 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200622 * @param string $table Table name
623 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000624 * @return bool
625 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200626 public function modify_column($table, $field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000627 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200628 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200629 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200630
Andrey Andreev24276a32012-01-08 02:44:38 +0200631 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000632 {
633 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200634 }
Barry Mienydd671972010-10-04 16:33:58 +0200635
Andrey Andreeva287a342012-11-05 23:19:59 +0200636 if (count($this->fields) === 0)
637 {
638 show_error('Field information is required.');
639 }
Barry Mienydd671972010-10-04 16:33:58 +0200640
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200641 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200642 $this->_reset();
643 if ($sqls === FALSE)
644 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200645 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200646 }
647
648 for ($i = 0, $c = count($sqls); $i < $c; $i++)
649 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200650 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000651 {
652 return FALSE;
653 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000654 }
Barry Mienydd671972010-10-04 16:33:58 +0200655
Robin Sowell8a54ef22009-03-04 14:49:53 +0000656 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000657 }
658
659 // --------------------------------------------------------------------
660
661 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200662 * ALTER TABLE
663 *
664 * @param string $alter_type ALTER type
665 * @param string $table Table name
666 * @param mixed $field Column definition
667 * @return string|string[]
668 */
669 protected function _alter_table($alter_type, $table, $field)
670 {
671 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
672
673 // DROP has everything it needs now.
674 if ($alter_type === 'DROP')
675 {
676 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
677 }
678
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200679 $sql .= ($alter_type === 'ADD')
680 ? 'ADD '
681 : $alter_type.' COLUMN ';
682
Andrey Andreeva287a342012-11-05 23:19:59 +0200683 $sqls = array();
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200684 for ($i = 0, $c = count($field); $i < $c; $i++)
Andrey Andreeva287a342012-11-05 23:19:59 +0200685 {
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
Andrey Andreev13943042014-03-17 11:50:45 +0200720 isset($attributes['TYPE']) && $this->_attr_type($attributes);
Andrey Andreeva287a342012-11-05 23:19:59 +0200721
722 $field = array(
Andrey Andreev13943042014-03-17 11:50:45 +0200723 'name' => $key,
724 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
725 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
726 'length' => '',
727 'unsigned' => '',
728 'null' => '',
729 'unique' => '',
730 'default' => '',
731 'auto_increment' => '',
732 '_literal' => FALSE
Andrey Andreeva287a342012-11-05 23:19:59 +0200733 );
734
Andrey Andreev13943042014-03-17 11:50:45 +0200735 isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
736
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200737 if ($create_table === FALSE)
738 {
739 if (isset($attributes['AFTER']))
740 {
Andrey Andreev96185a32013-10-28 16:04:02 +0200741 $field['after'] = $attributes['AFTER'];
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200742 }
743 elseif (isset($attributes['FIRST']))
744 {
745 $field['first'] = (bool) $attributes['FIRST'];
746 }
747 }
748
Andrey Andreeva287a342012-11-05 23:19:59 +0200749 $this->_attr_default($attributes, $field);
750
751 if (isset($attributes['NULL']))
752 {
753 if ($attributes['NULL'] === TRUE)
754 {
755 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
756 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200757 else
Andrey Andreeva287a342012-11-05 23:19:59 +0200758 {
759 $field['null'] = ' NOT NULL';
760 }
761 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200762 elseif ($create_table === TRUE)
763 {
764 $field['null'] = ' NOT NULL';
765 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200766
767 $this->_attr_auto_increment($attributes, $field);
768 $this->_attr_unique($attributes, $field);
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700769
770 if (isset($attributes['COMMENT']))
771 {
772 $field['comment'] = $this->db->escape($attributes['COMMENT']);
773 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200774
775 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
776 {
777 switch (strtoupper($attributes['TYPE']))
778 {
779 case 'ENUM':
780 case 'SET':
781 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200782 $field['length'] = is_array($attributes['CONSTRAINT'])
783 ? "('".implode("','", $attributes['CONSTRAINT'])."')"
784 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreevfde170c2014-03-10 19:55:11 +0200785 break;
Andrey Andreeva287a342012-11-05 23:19:59 +0200786 default:
787 $field['length'] = is_array($attributes['CONSTRAINT'])
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200788 ? '('.implode(',', $attributes['CONSTRAINT']).')'
789 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreeva287a342012-11-05 23:19:59 +0200790 break;
791 }
792 }
793
794 $fields[] = $field;
795 }
796
797 return $fields;
798 }
799
800 // --------------------------------------------------------------------
801
802 /**
803 * Process column
804 *
805 * @param array $field
806 * @return string
807 */
808 protected function _process_column($field)
809 {
810 return $this->db->escape_identifiers($field['name'])
811 .' '.$field['type'].$field['length']
812 .$field['unsigned']
813 .$field['default']
814 .$field['null']
815 .$field['auto_increment']
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700816 .$field['unique'];
Andrey Andreeva287a342012-11-05 23:19:59 +0200817 }
818
819 // --------------------------------------------------------------------
820
821 /**
822 * Field attribute TYPE
823 *
824 * Performs a data type mapping between different databases.
825 *
826 * @param array &$attributes
827 * @return void
828 */
829 protected function _attr_type(&$attributes)
830 {
831 // Usually overriden by drivers
832 }
833
834 // --------------------------------------------------------------------
835
836 /**
837 * Field attribute UNSIGNED
838 *
839 * Depending on the _unsigned property value:
840 *
841 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
842 * - FALSE will always set $field['unsigned'] to ''
843 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
844 * if $attributes['TYPE'] is found in the array
845 * - array(TYPE => UTYPE) will change $field['type'],
846 * from TYPE to UTYPE in case of a match
847 *
848 * @param array &$attributes
849 * @param array &$field
850 * @return void
851 */
852 protected function _attr_unsigned(&$attributes, &$field)
853 {
854 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
855 {
856 return;
857 }
858
859 // Reset the attribute in order to avoid issues if we do type conversion
860 $attributes['UNSIGNED'] = FALSE;
861
862 if (is_array($this->_unsigned))
863 {
864 foreach (array_keys($this->_unsigned) as $key)
865 {
866 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
867 {
868 $field['unsigned'] = ' UNSIGNED';
869 return;
870 }
871 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
872 {
873 $field['type'] = $key;
874 return;
875 }
876 }
877
878 return;
879 }
880
881 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
882 }
883
884 // --------------------------------------------------------------------
885
886 /**
887 * Field attribute DEFAULT
888 *
889 * @param array &$attributes
890 * @param array &$field
891 * @return void
892 */
893 protected function _attr_default(&$attributes, &$field)
894 {
895 if ($this->_default === FALSE)
896 {
897 return;
898 }
899
900 if (array_key_exists('DEFAULT', $attributes))
901 {
902 if ($attributes['DEFAULT'] === NULL)
903 {
904 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
905
906 // Override the NULL attribute if that's our default
Andrey Andreev22ce2762014-08-15 11:59:16 +0300907 $attributes['NULL'] = TRUE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200908 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
909 }
910 else
911 {
912 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
913 }
914 }
915 }
916
917 // --------------------------------------------------------------------
918
919 /**
920 * Field attribute UNIQUE
921 *
922 * @param array &$attributes
923 * @param array &$field
924 * @return void
925 */
926 protected function _attr_unique(&$attributes, &$field)
927 {
928 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
929 {
930 $field['unique'] = ' UNIQUE';
931 }
932 }
933
934 // --------------------------------------------------------------------
935
936 /**
937 * Field attribute AUTO_INCREMENT
938 *
939 * @param array &$attributes
940 * @param array &$field
941 * @return void
942 */
943 protected function _attr_auto_increment(&$attributes, &$field)
944 {
945 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
946 {
947 $field['auto_increment'] = ' AUTO_INCREMENT';
948 }
949 }
950
951 // --------------------------------------------------------------------
952
953 /**
954 * Process primary keys
955 *
956 * @param string $table Table name
957 * @return string
958 */
959 protected function _process_primary_keys($table)
960 {
961 $sql = '';
962
963 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
964 {
965 if ( ! isset($this->fields[$this->primary_keys[$i]]))
966 {
967 unset($this->primary_keys[$i]);
968 }
969 }
970
971 if (count($this->primary_keys) > 0)
972 {
973 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
974 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
975 }
976
977 return $sql;
978 }
979
980 // --------------------------------------------------------------------
981
982 /**
983 * Process indexes
984 *
985 * @param string $table
986 * @return string
987 */
Andrey Andreev35451022012-11-25 17:20:04 +0200988 protected function _process_indexes($table)
Andrey Andreeva287a342012-11-05 23:19:59 +0200989 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200990 $sqls = array();
991
992 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
993 {
Andrey Andreev35451022012-11-25 17:20:04 +0200994 if (is_array($this->keys[$i]))
995 {
996 for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
997 {
998 if ( ! isset($this->fields[$this->keys[$i][$i2]]))
999 {
1000 unset($this->keys[$i][$i2]);
1001 continue;
1002 }
1003 }
1004 }
1005 elseif ( ! isset($this->fields[$this->keys[$i]]))
Andrey Andreeva287a342012-11-05 23:19:59 +02001006 {
1007 unset($this->keys[$i]);
1008 continue;
1009 }
1010
1011 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
1012
mjnaderid3a6ca22013-12-19 01:35:57 +03301013 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
1014 .' ON '.$this->db->escape_identifiers($table)
Andrey Andreeva287a342012-11-05 23:19:59 +02001015 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
1016 }
1017
1018 return $sqls;
1019 }
1020
1021 // --------------------------------------------------------------------
1022
1023 /**
Derek Allard2067d1a2008-11-13 22:59:24 +00001024 * Reset
1025 *
1026 * Resets table creation vars
1027 *
Derek Allard2067d1a2008-11-13 22:59:24 +00001028 * @return void
1029 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +00001030 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +00001031 {
Andrey Andreev24276a32012-01-08 02:44:38 +02001032 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +00001033 }
1034
1035}
1036
1037/* End of file DB_forge.php */
Timothy Warren215890b2012-03-20 09:38:16 -04001038/* Location: ./system/database/DB_forge.php */