blob: f9cf76a14625e83ca449154217039cac78ca345c [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 *
Andrey Andreevfe9309d2015-01-09 17:48:58 +02005 * An open source application development framework for PHP
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 Andreevfe9309d2015-01-09 17:48:58 +02009 * Copyright (c) 2014 - 2015, 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 Andreevfe9309d2015-01-09 17:48:58 +020032 * @copyright Copyright (c) 2014 - 2015, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @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 /**
Calvin Tam55bc5052015-07-24 02:27:24 -0700146 * NULL value representation in CREATE/ALTER TABLE statements
Andrey Andreeva287a342012-11-05 23:19:59 +0200147 *
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 Andreev90726b82015-01-20 12:39:22 +0200170 log_message('info', '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 Andreev95f81572015-10-19 13:16:19 +0300242 // DO NOT change this! This condition is only applicable
243 // for PRIMARY keys because you can only have one such,
244 // and therefore all fields you add to it will be included
245 // in the same, composite PRIMARY KEY.
246 //
247 // It's not the same for regular indexes.
248 if ($primary === TRUE && is_array($key))
Derek Allard2067d1a2008-11-13 22:59:24 +0000249 {
Pascal Krietec3a4a8d2011-02-14 13:40:08 -0500250 foreach ($key as $one)
Derek Allard2067d1a2008-11-13 22:59:24 +0000251 {
252 $this->add_key($one, $primary);
253 }
Barry Mienydd671972010-10-04 16:33:58 +0200254
Andrey Andreeva287a342012-11-05 23:19:59 +0200255 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000256 }
Barry Mienydd671972010-10-04 16:33:58 +0200257
Derek Allard2067d1a2008-11-13 22:59:24 +0000258 if ($primary === TRUE)
259 {
260 $this->primary_keys[] = $key;
261 }
262 else
263 {
264 $this->keys[] = $key;
265 }
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000266
267 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000268 }
269
270 // --------------------------------------------------------------------
271
272 /**
273 * Add Field
274 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200275 * @param array $field
Andrew Podner4296a652012-12-17 07:51:15 -0500276 * @return CI_DB_forge
Derek Allard2067d1a2008-11-13 22:59:24 +0000277 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200278 public function add_field($field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000279 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000280 if (is_string($field))
281 {
Andrey Andreev24276a32012-01-08 02:44:38 +0200282 if ($field === 'id')
Derek Allard2067d1a2008-11-13 22:59:24 +0000283 {
284 $this->add_field(array(
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000285 'id' => array(
286 'type' => 'INT',
287 'constraint' => 9,
288 'auto_increment' => TRUE
289 )
290 ));
Derek Allard2067d1a2008-11-13 22:59:24 +0000291 $this->add_key('id', TRUE);
292 }
293 else
294 {
295 if (strpos($field, ' ') === FALSE)
296 {
297 show_error('Field information is required for that operation.');
298 }
Barry Mienydd671972010-10-04 16:33:58 +0200299
Derek Allard2067d1a2008-11-13 22:59:24 +0000300 $this->fields[] = $field;
301 }
302 }
Barry Mienydd671972010-10-04 16:33:58 +0200303
Derek Allard2067d1a2008-11-13 22:59:24 +0000304 if (is_array($field))
305 {
306 $this->fields = array_merge($this->fields, $field);
307 }
Andrey Andreev24276a32012-01-08 02:44:38 +0200308
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000309 return $this;
Derek Allard2067d1a2008-11-13 22:59:24 +0000310 }
311
312 // --------------------------------------------------------------------
313
314 /**
315 * Create Table
316 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200317 * @param string $table Table name
318 * @param bool $if_not_exists Whether to add IF NOT EXISTS condition
Andrey Andreev27f798b2014-01-20 18:19:13 +0200319 * @param array $attributes Associative array of table attributes
Derek Allard2067d1a2008-11-13 22:59:24 +0000320 * @return bool
321 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200322 public function create_table($table, $if_not_exists = FALSE, array $attributes = array())
Barry Mienydd671972010-10-04 16:33:58 +0200323 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100324 if ($table === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000325 {
326 show_error('A table name is required for that operation.');
327 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200328 else
329 {
330 $table = $this->db->dbprefix.$table;
331 }
Barry Mienydd671972010-10-04 16:33:58 +0200332
Andrey Andreev24276a32012-01-08 02:44:38 +0200333 if (count($this->fields) === 0)
Barry Mienydd671972010-10-04 16:33:58 +0200334 {
Derek Allard2067d1a2008-11-13 22:59:24 +0000335 show_error('Field information is required.');
336 }
337
Andrey Andreev27f798b2014-01-20 18:19:13 +0200338 $sql = $this->_create_table($table, $if_not_exists, $attributes);
Andrey Andreev5d281762012-06-11 22:05:40 +0300339
340 if (is_bool($sql))
341 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200342 $this->_reset();
343 if ($sql === FALSE)
344 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200345 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200346 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300347 }
348
Andrey Andreeva287a342012-11-05 23:19:59 +0200349 if (($result = $this->db->query($sql)) !== FALSE)
Andrey Andreev5d281762012-06-11 22:05:40 +0300350 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200351 empty($this->db->data_cache['table_names']) OR $this->db->data_cache['table_names'][] = $table;
352
353 // Most databases don't support creating indexes from within the CREATE TABLE statement
354 if ( ! empty($this->keys))
355 {
356 for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
357 {
358 $this->db->query($sqls[$i]);
359 }
360 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300361 }
362
Andrey Andreeva287a342012-11-05 23:19:59 +0200363 $this->_reset();
Andrey Andreev5d281762012-06-11 22:05:40 +0300364 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000365 }
366
367 // --------------------------------------------------------------------
368
369 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200370 * Create Table
371 *
372 * @param string $table Table name
373 * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
Andrey Andreev27f798b2014-01-20 18:19:13 +0200374 * @param array $attributes Associative array of table attributes
Andrey Andreeva287a342012-11-05 23:19:59 +0200375 * @return mixed
376 */
Andrey Andreev27f798b2014-01-20 18:19:13 +0200377 protected function _create_table($table, $if_not_exists, $attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200378 {
379 if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
380 {
381 if ($this->db->table_exists($table))
382 {
383 return TRUE;
384 }
385 else
386 {
387 $if_not_exists = FALSE;
388 }
389 }
390
391 $sql = ($if_not_exists)
392 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
393 : 'CREATE TABLE';
394
395 $columns = $this->_process_fields(TRUE);
396 for ($i = 0, $c = count($columns); $i < $c; $i++)
397 {
398 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
399 ? "\n\t".$columns[$i]['_literal']
400 : "\n\t".$this->_process_column($columns[$i]);
401 }
402
403 $columns = implode(',', $columns)
404 .$this->_process_primary_keys($table);
405
406 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
407 if ($this->_create_table_keys === TRUE)
408 {
Andrey Andreev35451022012-11-25 17:20:04 +0200409 $columns .= $this->_process_indexes($table);
Andrey Andreeva287a342012-11-05 23:19:59 +0200410 }
411
412 // _create_table will usually have the following format: "%s %s (%s\n)"
Andrey Andreevaaca5cb2014-03-31 17:20:55 +0300413 $sql = sprintf($this->_create_table.'%s',
Andrey Andreeva287a342012-11-05 23:19:59 +0200414 $sql,
415 $this->db->escape_identifiers($table),
Andrey Andreev27f798b2014-01-20 18:19:13 +0200416 $columns,
417 $this->_create_table_attr($attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200418 );
419
420 return $sql;
421 }
422
423 // --------------------------------------------------------------------
424
425 /**
Andrey Andreev27f798b2014-01-20 18:19:13 +0200426 * CREATE TABLE attributes
427 *
428 * @param array $attributes Associative array of table attributes
429 * @return string
430 */
431 protected function _create_table_attr($attributes)
432 {
433 $sql = '';
434
435 foreach (array_keys($attributes) as $key)
436 {
437 if (is_string($key))
438 {
439 $sql .= ' '.strtoupper($key).' '.$attributes[$key];
440 }
441 }
442
443 return $sql;
444 }
445
446 // --------------------------------------------------------------------
447
448 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000449 * Drop Table
450 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200451 * @param string $table_name Table name
452 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000453 * @return bool
454 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200455 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000456 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100457 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300458 {
459 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
460 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200461
Andrey Andreev441d3532015-07-02 11:34:20 +0300462 if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
Andrey Andreeva287a342012-11-05 23:19:59 +0200463 {
464 return TRUE;
465 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300466
Andrey Andreeva287a342012-11-05 23:19:59 +0200467 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300468
469 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200470 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300471 {
472 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
473 if ($key !== FALSE)
474 {
475 unset($this->db->data_cache['table_names'][$key]);
476 }
477 }
478
Andrey Andreeva287a342012-11-05 23:19:59 +0200479 return $query;
480 }
481
482 // --------------------------------------------------------------------
483
484 /**
485 * Drop Table
486 *
487 * Generates a platform-specific DROP TABLE string
488 *
489 * @param string $table Table name
490 * @param bool $if_exists Whether to add an IF EXISTS condition
491 * @return string
492 */
493 protected function _drop_table($table, $if_exists)
494 {
495 $sql = 'DROP TABLE';
496
497 if ($if_exists)
498 {
499 if ($this->_drop_table_if === FALSE)
500 {
501 if ( ! $this->db->table_exists($table))
502 {
503 return TRUE;
504 }
505 }
506 else
507 {
508 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
509 }
510 }
511
512 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000513 }
514
515 // --------------------------------------------------------------------
516
517 /**
518 * Rename Table
519 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200520 * @param string $table_name Old table name
521 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000522 * @return bool
523 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000524 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000525 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100526 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000527 {
528 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300529 return FALSE;
530 }
531 elseif ($this->_rename_table === FALSE)
532 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200533 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000534 }
Barry Mienydd671972010-10-04 16:33:58 +0200535
Andrey Andreev5d281762012-06-11 22:05:40 +0300536 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300537 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
538 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
539 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300540
541 if ($result && ! empty($this->db->data_cache['table_names']))
542 {
543 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
544 if ($key !== FALSE)
545 {
546 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
547 }
548 }
549
550 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000551 }
552
553 // --------------------------------------------------------------------
554
555 /**
556 * Column Add
557 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200558 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200559 * @param string $table Table name
560 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200561 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000562 * @return bool
563 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200564 public function add_column($table, $field, $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000565 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200566 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200567 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200568
Andrey Andreev24276a32012-01-08 02:44:38 +0200569 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200570 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200571 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
572 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
573 {
574 $field[$k]['after'] = $_after;
575 }
576
Barry Mienydd671972010-10-04 16:33:58 +0200577 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200578 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000579
Andrey Andreeva287a342012-11-05 23:19:59 +0200580 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
581 $this->_reset();
582 if ($sqls === FALSE)
583 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200584 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200585 }
Barry Mienydd671972010-10-04 16:33:58 +0200586
Andrey Andreeva287a342012-11-05 23:19:59 +0200587 for ($i = 0, $c = count($sqls); $i < $c; $i++)
588 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200589 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000590 {
591 return FALSE;
592 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000593 }
Barry Mienydd671972010-10-04 16:33:58 +0200594
Robin Sowell8a54ef22009-03-04 14:49:53 +0000595 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000596 }
597
598 // --------------------------------------------------------------------
599
600 /**
601 * Column Drop
602 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200603 * @param string $table Table name
604 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000605 * @return bool
606 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200607 public function drop_column($table, $column_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000608 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200609 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
610 if ($sql === FALSE)
611 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200612 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200613 }
614
615 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000616 }
617
618 // --------------------------------------------------------------------
619
620 /**
621 * Column Modify
622 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200623 * @param string $table Table name
624 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000625 * @return bool
626 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200627 public function modify_column($table, $field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000628 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200629 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200630 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200631
Andrey Andreev24276a32012-01-08 02:44:38 +0200632 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000633 {
634 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200635 }
Barry Mienydd671972010-10-04 16:33:58 +0200636
Andrey Andreeva287a342012-11-05 23:19:59 +0200637 if (count($this->fields) === 0)
638 {
639 show_error('Field information is required.');
640 }
Barry Mienydd671972010-10-04 16:33:58 +0200641
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200642 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200643 $this->_reset();
644 if ($sqls === FALSE)
645 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200646 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200647 }
648
649 for ($i = 0, $c = count($sqls); $i < $c; $i++)
650 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200651 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000652 {
653 return FALSE;
654 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000655 }
Barry Mienydd671972010-10-04 16:33:58 +0200656
Robin Sowell8a54ef22009-03-04 14:49:53 +0000657 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000658 }
659
660 // --------------------------------------------------------------------
661
662 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200663 * ALTER TABLE
664 *
665 * @param string $alter_type ALTER type
666 * @param string $table Table name
667 * @param mixed $field Column definition
668 * @return string|string[]
669 */
670 protected function _alter_table($alter_type, $table, $field)
671 {
672 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
673
674 // DROP has everything it needs now.
675 if ($alter_type === 'DROP')
676 {
677 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
678 }
679
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200680 $sql .= ($alter_type === 'ADD')
681 ? 'ADD '
682 : $alter_type.' COLUMN ';
683
Andrey Andreeva287a342012-11-05 23:19:59 +0200684 $sqls = array();
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200685 for ($i = 0, $c = count($field); $i < $c; $i++)
Andrey Andreeva287a342012-11-05 23:19:59 +0200686 {
687 $sqls[] = $sql
688 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
689 }
690
691 return $sqls;
692 }
693
694 // --------------------------------------------------------------------
695
696 /**
697 * Process fields
698 *
699 * @param bool $create_table
700 * @return array
701 */
702 protected function _process_fields($create_table = FALSE)
703 {
704 $fields = array();
705
706 foreach ($this->fields as $key => $attributes)
707 {
708 if (is_int($key) && ! is_array($attributes))
709 {
710 $fields[] = array('_literal' => $attributes);
711 continue;
712 }
713
714 $attributes = array_change_key_case($attributes, CASE_UPPER);
715
716 if ($create_table === TRUE && empty($attributes['TYPE']))
717 {
718 continue;
719 }
720
Andrey Andreev13943042014-03-17 11:50:45 +0200721 isset($attributes['TYPE']) && $this->_attr_type($attributes);
Andrey Andreeva287a342012-11-05 23:19:59 +0200722
723 $field = array(
Andrey Andreev13943042014-03-17 11:50:45 +0200724 'name' => $key,
725 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
726 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
727 'length' => '',
728 'unsigned' => '',
729 'null' => '',
730 'unique' => '',
731 'default' => '',
732 'auto_increment' => '',
733 '_literal' => FALSE
Andrey Andreeva287a342012-11-05 23:19:59 +0200734 );
735
Andrey Andreev13943042014-03-17 11:50:45 +0200736 isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
737
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200738 if ($create_table === FALSE)
739 {
740 if (isset($attributes['AFTER']))
741 {
Andrey Andreev96185a32013-10-28 16:04:02 +0200742 $field['after'] = $attributes['AFTER'];
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200743 }
744 elseif (isset($attributes['FIRST']))
745 {
746 $field['first'] = (bool) $attributes['FIRST'];
747 }
748 }
749
Andrey Andreeva287a342012-11-05 23:19:59 +0200750 $this->_attr_default($attributes, $field);
751
752 if (isset($attributes['NULL']))
753 {
754 if ($attributes['NULL'] === TRUE)
755 {
756 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
757 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200758 else
Andrey Andreeva287a342012-11-05 23:19:59 +0200759 {
760 $field['null'] = ' NOT NULL';
761 }
762 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200763 elseif ($create_table === TRUE)
764 {
765 $field['null'] = ' NOT NULL';
766 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200767
768 $this->_attr_auto_increment($attributes, $field);
769 $this->_attr_unique($attributes, $field);
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700770
771 if (isset($attributes['COMMENT']))
772 {
773 $field['comment'] = $this->db->escape($attributes['COMMENT']);
774 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200775
776 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
777 {
778 switch (strtoupper($attributes['TYPE']))
779 {
780 case 'ENUM':
781 case 'SET':
782 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200783 $field['length'] = is_array($attributes['CONSTRAINT'])
784 ? "('".implode("','", $attributes['CONSTRAINT'])."')"
785 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreevfde170c2014-03-10 19:55:11 +0200786 break;
Andrey Andreeva287a342012-11-05 23:19:59 +0200787 default:
788 $field['length'] = is_array($attributes['CONSTRAINT'])
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200789 ? '('.implode(',', $attributes['CONSTRAINT']).')'
790 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreeva287a342012-11-05 23:19:59 +0200791 break;
792 }
793 }
794
795 $fields[] = $field;
796 }
797
798 return $fields;
799 }
800
801 // --------------------------------------------------------------------
802
803 /**
804 * Process column
805 *
806 * @param array $field
807 * @return string
808 */
809 protected function _process_column($field)
810 {
811 return $this->db->escape_identifiers($field['name'])
812 .' '.$field['type'].$field['length']
813 .$field['unsigned']
814 .$field['default']
815 .$field['null']
816 .$field['auto_increment']
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700817 .$field['unique'];
Andrey Andreeva287a342012-11-05 23:19:59 +0200818 }
819
820 // --------------------------------------------------------------------
821
822 /**
823 * Field attribute TYPE
824 *
825 * Performs a data type mapping between different databases.
826 *
827 * @param array &$attributes
828 * @return void
829 */
830 protected function _attr_type(&$attributes)
831 {
Claudio Galdioloba6bd222015-01-29 11:43:25 -0500832 // Usually overridden by drivers
Andrey Andreeva287a342012-11-05 23:19:59 +0200833 }
834
835 // --------------------------------------------------------------------
836
837 /**
838 * Field attribute UNSIGNED
839 *
840 * Depending on the _unsigned property value:
841 *
842 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
843 * - FALSE will always set $field['unsigned'] to ''
844 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
845 * if $attributes['TYPE'] is found in the array
846 * - array(TYPE => UTYPE) will change $field['type'],
847 * from TYPE to UTYPE in case of a match
848 *
849 * @param array &$attributes
850 * @param array &$field
851 * @return void
852 */
853 protected function _attr_unsigned(&$attributes, &$field)
854 {
855 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
856 {
857 return;
858 }
859
860 // Reset the attribute in order to avoid issues if we do type conversion
861 $attributes['UNSIGNED'] = FALSE;
862
863 if (is_array($this->_unsigned))
864 {
865 foreach (array_keys($this->_unsigned) as $key)
866 {
867 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
868 {
869 $field['unsigned'] = ' UNSIGNED';
870 return;
871 }
872 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
873 {
874 $field['type'] = $key;
875 return;
876 }
877 }
878
879 return;
880 }
881
882 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
883 }
884
885 // --------------------------------------------------------------------
886
887 /**
888 * Field attribute DEFAULT
889 *
890 * @param array &$attributes
891 * @param array &$field
892 * @return void
893 */
894 protected function _attr_default(&$attributes, &$field)
895 {
896 if ($this->_default === FALSE)
897 {
898 return;
899 }
900
901 if (array_key_exists('DEFAULT', $attributes))
902 {
903 if ($attributes['DEFAULT'] === NULL)
904 {
905 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
906
907 // Override the NULL attribute if that's our default
Andrey Andreev22ce2762014-08-15 11:59:16 +0300908 $attributes['NULL'] = TRUE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200909 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
910 }
911 else
912 {
913 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
914 }
915 }
916 }
917
918 // --------------------------------------------------------------------
919
920 /**
921 * Field attribute UNIQUE
922 *
923 * @param array &$attributes
924 * @param array &$field
925 * @return void
926 */
927 protected function _attr_unique(&$attributes, &$field)
928 {
929 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
930 {
931 $field['unique'] = ' UNIQUE';
932 }
933 }
934
935 // --------------------------------------------------------------------
936
937 /**
938 * Field attribute AUTO_INCREMENT
939 *
940 * @param array &$attributes
941 * @param array &$field
942 * @return void
943 */
944 protected function _attr_auto_increment(&$attributes, &$field)
945 {
946 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
947 {
948 $field['auto_increment'] = ' AUTO_INCREMENT';
949 }
950 }
951
952 // --------------------------------------------------------------------
953
954 /**
955 * Process primary keys
956 *
957 * @param string $table Table name
958 * @return string
959 */
960 protected function _process_primary_keys($table)
961 {
962 $sql = '';
963
964 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
965 {
966 if ( ! isset($this->fields[$this->primary_keys[$i]]))
967 {
968 unset($this->primary_keys[$i]);
969 }
970 }
971
972 if (count($this->primary_keys) > 0)
973 {
974 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
975 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
976 }
977
978 return $sql;
979 }
980
981 // --------------------------------------------------------------------
982
983 /**
984 * Process indexes
985 *
986 * @param string $table
987 * @return string
988 */
Andrey Andreev35451022012-11-25 17:20:04 +0200989 protected function _process_indexes($table)
Andrey Andreeva287a342012-11-05 23:19:59 +0200990 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200991 $sqls = array();
992
993 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
994 {
Andrey Andreev35451022012-11-25 17:20:04 +0200995 if (is_array($this->keys[$i]))
996 {
997 for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
998 {
999 if ( ! isset($this->fields[$this->keys[$i][$i2]]))
1000 {
1001 unset($this->keys[$i][$i2]);
1002 continue;
1003 }
1004 }
1005 }
1006 elseif ( ! isset($this->fields[$this->keys[$i]]))
Andrey Andreeva287a342012-11-05 23:19:59 +02001007 {
1008 unset($this->keys[$i]);
1009 continue;
1010 }
1011
1012 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
1013
mjnaderid3a6ca22013-12-19 01:35:57 +03301014 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
1015 .' ON '.$this->db->escape_identifiers($table)
Andrey Andreeva287a342012-11-05 23:19:59 +02001016 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
1017 }
1018
1019 return $sqls;
1020 }
1021
1022 // --------------------------------------------------------------------
1023
1024 /**
Derek Allard2067d1a2008-11-13 22:59:24 +00001025 * Reset
1026 *
1027 * Resets table creation vars
1028 *
Derek Allard2067d1a2008-11-13 22:59:24 +00001029 * @return void
1030 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +00001031 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +00001032 {
Andrey Andreev24276a32012-01-08 02:44:38 +02001033 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +00001034 }
1035
1036}