blob: dde285598026a0d29293523ecfd993cc76a61b57 [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 {
Adrian Voicu7cc6cea2015-07-10 14:41:25 +0300242 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
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
Andrey Andreev441d3532015-07-02 11:34:20 +0300456 if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
Andrey Andreeva287a342012-11-05 23:19:59 +0200457 {
458 return TRUE;
459 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300460
Andrey Andreeva287a342012-11-05 23:19:59 +0200461 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300462
463 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200464 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300465 {
466 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
467 if ($key !== FALSE)
468 {
469 unset($this->db->data_cache['table_names'][$key]);
470 }
471 }
472
Andrey Andreeva287a342012-11-05 23:19:59 +0200473 return $query;
474 }
475
476 // --------------------------------------------------------------------
477
478 /**
479 * Drop Table
480 *
481 * Generates a platform-specific DROP TABLE string
482 *
483 * @param string $table Table name
484 * @param bool $if_exists Whether to add an IF EXISTS condition
485 * @return string
486 */
487 protected function _drop_table($table, $if_exists)
488 {
489 $sql = 'DROP TABLE';
490
491 if ($if_exists)
492 {
493 if ($this->_drop_table_if === FALSE)
494 {
495 if ( ! $this->db->table_exists($table))
496 {
497 return TRUE;
498 }
499 }
500 else
501 {
502 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
503 }
504 }
505
506 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000507 }
508
509 // --------------------------------------------------------------------
510
511 /**
512 * Rename Table
513 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200514 * @param string $table_name Old table name
515 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000516 * @return bool
517 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000518 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000519 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100520 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000521 {
522 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300523 return FALSE;
524 }
525 elseif ($this->_rename_table === FALSE)
526 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200527 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 }
Barry Mienydd671972010-10-04 16:33:58 +0200529
Andrey Andreev5d281762012-06-11 22:05:40 +0300530 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300531 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
532 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
533 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300534
535 if ($result && ! empty($this->db->data_cache['table_names']))
536 {
537 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
538 if ($key !== FALSE)
539 {
540 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
541 }
542 }
543
544 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000545 }
546
547 // --------------------------------------------------------------------
548
549 /**
550 * Column Add
551 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200552 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200553 * @param string $table Table name
554 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200555 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000556 * @return bool
557 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200558 public function add_column($table, $field, $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000559 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200560 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200561 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200562
Andrey Andreev24276a32012-01-08 02:44:38 +0200563 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200564 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200565 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
566 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
567 {
568 $field[$k]['after'] = $_after;
569 }
570
Barry Mienydd671972010-10-04 16:33:58 +0200571 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200572 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000573
Andrey Andreeva287a342012-11-05 23:19:59 +0200574 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
575 $this->_reset();
576 if ($sqls === FALSE)
577 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200578 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200579 }
Barry Mienydd671972010-10-04 16:33:58 +0200580
Andrey Andreeva287a342012-11-05 23:19:59 +0200581 for ($i = 0, $c = count($sqls); $i < $c; $i++)
582 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200583 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000584 {
585 return FALSE;
586 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000587 }
Barry Mienydd671972010-10-04 16:33:58 +0200588
Robin Sowell8a54ef22009-03-04 14:49:53 +0000589 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000590 }
591
592 // --------------------------------------------------------------------
593
594 /**
595 * Column Drop
596 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200597 * @param string $table Table name
598 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000599 * @return bool
600 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200601 public function drop_column($table, $column_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000602 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200603 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
604 if ($sql === FALSE)
605 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200606 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200607 }
608
609 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000610 }
611
612 // --------------------------------------------------------------------
613
614 /**
615 * Column Modify
616 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200617 * @param string $table Table name
618 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000619 * @return bool
620 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200621 public function modify_column($table, $field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000622 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200623 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200624 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200625
Andrey Andreev24276a32012-01-08 02:44:38 +0200626 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000627 {
628 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200629 }
Barry Mienydd671972010-10-04 16:33:58 +0200630
Andrey Andreeva287a342012-11-05 23:19:59 +0200631 if (count($this->fields) === 0)
632 {
633 show_error('Field information is required.');
634 }
Barry Mienydd671972010-10-04 16:33:58 +0200635
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200636 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200637 $this->_reset();
638 if ($sqls === FALSE)
639 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200640 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200641 }
642
643 for ($i = 0, $c = count($sqls); $i < $c; $i++)
644 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200645 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000646 {
647 return FALSE;
648 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000649 }
Barry Mienydd671972010-10-04 16:33:58 +0200650
Robin Sowell8a54ef22009-03-04 14:49:53 +0000651 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000652 }
653
654 // --------------------------------------------------------------------
655
656 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200657 * ALTER TABLE
658 *
659 * @param string $alter_type ALTER type
660 * @param string $table Table name
661 * @param mixed $field Column definition
662 * @return string|string[]
663 */
664 protected function _alter_table($alter_type, $table, $field)
665 {
666 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
667
668 // DROP has everything it needs now.
669 if ($alter_type === 'DROP')
670 {
671 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
672 }
673
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200674 $sql .= ($alter_type === 'ADD')
675 ? 'ADD '
676 : $alter_type.' COLUMN ';
677
Andrey Andreeva287a342012-11-05 23:19:59 +0200678 $sqls = array();
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200679 for ($i = 0, $c = count($field); $i < $c; $i++)
Andrey Andreeva287a342012-11-05 23:19:59 +0200680 {
681 $sqls[] = $sql
682 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
683 }
684
685 return $sqls;
686 }
687
688 // --------------------------------------------------------------------
689
690 /**
691 * Process fields
692 *
693 * @param bool $create_table
694 * @return array
695 */
696 protected function _process_fields($create_table = FALSE)
697 {
698 $fields = array();
699
700 foreach ($this->fields as $key => $attributes)
701 {
702 if (is_int($key) && ! is_array($attributes))
703 {
704 $fields[] = array('_literal' => $attributes);
705 continue;
706 }
707
708 $attributes = array_change_key_case($attributes, CASE_UPPER);
709
710 if ($create_table === TRUE && empty($attributes['TYPE']))
711 {
712 continue;
713 }
714
Andrey Andreev13943042014-03-17 11:50:45 +0200715 isset($attributes['TYPE']) && $this->_attr_type($attributes);
Andrey Andreeva287a342012-11-05 23:19:59 +0200716
717 $field = array(
Andrey Andreev13943042014-03-17 11:50:45 +0200718 'name' => $key,
719 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
720 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
721 'length' => '',
722 'unsigned' => '',
723 'null' => '',
724 'unique' => '',
725 'default' => '',
726 'auto_increment' => '',
727 '_literal' => FALSE
Andrey Andreeva287a342012-11-05 23:19:59 +0200728 );
729
Andrey Andreev13943042014-03-17 11:50:45 +0200730 isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
731
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200732 if ($create_table === FALSE)
733 {
734 if (isset($attributes['AFTER']))
735 {
Andrey Andreev96185a32013-10-28 16:04:02 +0200736 $field['after'] = $attributes['AFTER'];
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200737 }
738 elseif (isset($attributes['FIRST']))
739 {
740 $field['first'] = (bool) $attributes['FIRST'];
741 }
742 }
743
Andrey Andreeva287a342012-11-05 23:19:59 +0200744 $this->_attr_default($attributes, $field);
745
746 if (isset($attributes['NULL']))
747 {
748 if ($attributes['NULL'] === TRUE)
749 {
750 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
751 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200752 else
Andrey Andreeva287a342012-11-05 23:19:59 +0200753 {
754 $field['null'] = ' NOT NULL';
755 }
756 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200757 elseif ($create_table === TRUE)
758 {
759 $field['null'] = ' NOT NULL';
760 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200761
762 $this->_attr_auto_increment($attributes, $field);
763 $this->_attr_unique($attributes, $field);
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700764
765 if (isset($attributes['COMMENT']))
766 {
767 $field['comment'] = $this->db->escape($attributes['COMMENT']);
768 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200769
770 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
771 {
772 switch (strtoupper($attributes['TYPE']))
773 {
774 case 'ENUM':
775 case 'SET':
776 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200777 $field['length'] = is_array($attributes['CONSTRAINT'])
778 ? "('".implode("','", $attributes['CONSTRAINT'])."')"
779 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreevfde170c2014-03-10 19:55:11 +0200780 break;
Andrey Andreeva287a342012-11-05 23:19:59 +0200781 default:
782 $field['length'] = is_array($attributes['CONSTRAINT'])
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200783 ? '('.implode(',', $attributes['CONSTRAINT']).')'
784 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreeva287a342012-11-05 23:19:59 +0200785 break;
786 }
787 }
788
789 $fields[] = $field;
790 }
791
792 return $fields;
793 }
794
795 // --------------------------------------------------------------------
796
797 /**
798 * Process column
799 *
800 * @param array $field
801 * @return string
802 */
803 protected function _process_column($field)
804 {
805 return $this->db->escape_identifiers($field['name'])
806 .' '.$field['type'].$field['length']
807 .$field['unsigned']
808 .$field['default']
809 .$field['null']
810 .$field['auto_increment']
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700811 .$field['unique'];
Andrey Andreeva287a342012-11-05 23:19:59 +0200812 }
813
814 // --------------------------------------------------------------------
815
816 /**
817 * Field attribute TYPE
818 *
819 * Performs a data type mapping between different databases.
820 *
821 * @param array &$attributes
822 * @return void
823 */
824 protected function _attr_type(&$attributes)
825 {
Claudio Galdioloba6bd222015-01-29 11:43:25 -0500826 // Usually overridden by drivers
Andrey Andreeva287a342012-11-05 23:19:59 +0200827 }
828
829 // --------------------------------------------------------------------
830
831 /**
832 * Field attribute UNSIGNED
833 *
834 * Depending on the _unsigned property value:
835 *
836 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
837 * - FALSE will always set $field['unsigned'] to ''
838 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
839 * if $attributes['TYPE'] is found in the array
840 * - array(TYPE => UTYPE) will change $field['type'],
841 * from TYPE to UTYPE in case of a match
842 *
843 * @param array &$attributes
844 * @param array &$field
845 * @return void
846 */
847 protected function _attr_unsigned(&$attributes, &$field)
848 {
849 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
850 {
851 return;
852 }
853
854 // Reset the attribute in order to avoid issues if we do type conversion
855 $attributes['UNSIGNED'] = FALSE;
856
857 if (is_array($this->_unsigned))
858 {
859 foreach (array_keys($this->_unsigned) as $key)
860 {
861 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
862 {
863 $field['unsigned'] = ' UNSIGNED';
864 return;
865 }
866 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
867 {
868 $field['type'] = $key;
869 return;
870 }
871 }
872
873 return;
874 }
875
876 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
877 }
878
879 // --------------------------------------------------------------------
880
881 /**
882 * Field attribute DEFAULT
883 *
884 * @param array &$attributes
885 * @param array &$field
886 * @return void
887 */
888 protected function _attr_default(&$attributes, &$field)
889 {
890 if ($this->_default === FALSE)
891 {
892 return;
893 }
894
895 if (array_key_exists('DEFAULT', $attributes))
896 {
897 if ($attributes['DEFAULT'] === NULL)
898 {
899 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
900
901 // Override the NULL attribute if that's our default
Andrey Andreev22ce2762014-08-15 11:59:16 +0300902 $attributes['NULL'] = TRUE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200903 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
904 }
905 else
906 {
907 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
908 }
909 }
910 }
911
912 // --------------------------------------------------------------------
913
914 /**
915 * Field attribute UNIQUE
916 *
917 * @param array &$attributes
918 * @param array &$field
919 * @return void
920 */
921 protected function _attr_unique(&$attributes, &$field)
922 {
923 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
924 {
925 $field['unique'] = ' UNIQUE';
926 }
927 }
928
929 // --------------------------------------------------------------------
930
931 /**
932 * Field attribute AUTO_INCREMENT
933 *
934 * @param array &$attributes
935 * @param array &$field
936 * @return void
937 */
938 protected function _attr_auto_increment(&$attributes, &$field)
939 {
940 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
941 {
942 $field['auto_increment'] = ' AUTO_INCREMENT';
943 }
944 }
945
946 // --------------------------------------------------------------------
947
948 /**
949 * Process primary keys
950 *
951 * @param string $table Table name
952 * @return string
953 */
954 protected function _process_primary_keys($table)
955 {
956 $sql = '';
957
958 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
959 {
960 if ( ! isset($this->fields[$this->primary_keys[$i]]))
961 {
962 unset($this->primary_keys[$i]);
963 }
964 }
965
966 if (count($this->primary_keys) > 0)
967 {
968 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
969 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
970 }
971
972 return $sql;
973 }
974
975 // --------------------------------------------------------------------
976
977 /**
978 * Process indexes
979 *
980 * @param string $table
981 * @return string
982 */
Andrey Andreev35451022012-11-25 17:20:04 +0200983 protected function _process_indexes($table)
Andrey Andreeva287a342012-11-05 23:19:59 +0200984 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200985 $sqls = array();
986
987 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
988 {
Andrey Andreev35451022012-11-25 17:20:04 +0200989 if (is_array($this->keys[$i]))
990 {
991 for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
992 {
993 if ( ! isset($this->fields[$this->keys[$i][$i2]]))
994 {
995 unset($this->keys[$i][$i2]);
996 continue;
997 }
998 }
999 }
1000 elseif ( ! isset($this->fields[$this->keys[$i]]))
Andrey Andreeva287a342012-11-05 23:19:59 +02001001 {
1002 unset($this->keys[$i]);
1003 continue;
1004 }
1005
1006 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
1007
mjnaderid3a6ca22013-12-19 01:35:57 +03301008 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
1009 .' ON '.$this->db->escape_identifiers($table)
Andrey Andreeva287a342012-11-05 23:19:59 +02001010 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
1011 }
1012
1013 return $sqls;
1014 }
1015
1016 // --------------------------------------------------------------------
1017
1018 /**
Derek Allard2067d1a2008-11-13 22:59:24 +00001019 * Reset
1020 *
1021 * Resets table creation vars
1022 *
Derek Allard2067d1a2008-11-13 22:59:24 +00001023 * @return void
1024 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +00001025 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +00001026 {
Andrey Andreev24276a32012-01-08 02:44:38 +02001027 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +00001028 }
1029
1030}