blob: 4d23e4e7bf20f086a81e5b053d22e9a440afd558 [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 Andreevcce6bd12018-01-09 11:32:02 +02009 * Copyright (c) 2014 - 2018, 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
Andrey Andreev1924e872016-01-11 12:55:34 +020031 * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
Andrey Andreevcce6bd12018-01-09 11:32:02 +020032 * @copyright Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020033 * @license http://opensource.org/licenses/MIT MIT License
Andrey Andreevbd202c92016-01-11 12:50:18 +020034 * @link https://codeigniter.com
Andrey Andreevbdb96ca2014-10-28 00:13:31 +020035 * @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
Andrey Andreevbd202c92016-01-11 12:50:18 +020045 * @link https://codeigniter.com/user_guide/database/
Derek Allard2067d1a2008-11-13 22:59:24 +000046 */
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 }
Andrey Andreevda270b22016-10-17 18:22:43 +0300187 elseif ( ! $this->db->query(sprintf($this->_create_database, $this->db->escape_identifiers($db_name), $this->db->char_set, $this->db->dbcollat)))
Andrey Andreevd947eba2012-04-09 14:58:28 +0300188 {
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 }
Andrey Andreevda270b22016-10-17 18:22:43 +0300214 elseif ( ! $this->db->query(sprintf($this->_drop_database, $this->db->escape_identifiers($db_name))))
Andrey Andreevd947eba2012-04-09 14:58:28 +0300215 {
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 Andreev89288a42018-09-11 17:29:56 +0300351 if (isset($this->db->data_cache['table_names']))
352 {
353 $this->db->data_cache['table_names'][] = $table;
354 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200355
356 // Most databases don't support creating indexes from within the CREATE TABLE statement
357 if ( ! empty($this->keys))
358 {
359 for ($i = 0, $sqls = $this->_process_indexes($table), $c = count($sqls); $i < $c; $i++)
360 {
361 $this->db->query($sqls[$i]);
362 }
363 }
Andrey Andreev5d281762012-06-11 22:05:40 +0300364 }
365
Andrey Andreeva287a342012-11-05 23:19:59 +0200366 $this->_reset();
Andrey Andreev5d281762012-06-11 22:05:40 +0300367 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000368 }
369
370 // --------------------------------------------------------------------
371
372 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200373 * Create Table
374 *
375 * @param string $table Table name
376 * @param bool $if_not_exists Whether to add 'IF NOT EXISTS' condition
Andrey Andreev27f798b2014-01-20 18:19:13 +0200377 * @param array $attributes Associative array of table attributes
Andrey Andreeva287a342012-11-05 23:19:59 +0200378 * @return mixed
379 */
Andrey Andreev27f798b2014-01-20 18:19:13 +0200380 protected function _create_table($table, $if_not_exists, $attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200381 {
382 if ($if_not_exists === TRUE && $this->_create_table_if === FALSE)
383 {
384 if ($this->db->table_exists($table))
385 {
386 return TRUE;
387 }
Andrey Andreevfbe4d792017-12-27 19:49:03 +0200388
389 $if_not_exists = FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200390 }
391
392 $sql = ($if_not_exists)
393 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
394 : 'CREATE TABLE';
395
396 $columns = $this->_process_fields(TRUE);
397 for ($i = 0, $c = count($columns); $i < $c; $i++)
398 {
399 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
400 ? "\n\t".$columns[$i]['_literal']
401 : "\n\t".$this->_process_column($columns[$i]);
402 }
403
404 $columns = implode(',', $columns)
405 .$this->_process_primary_keys($table);
406
407 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
408 if ($this->_create_table_keys === TRUE)
409 {
Andrey Andreev35451022012-11-25 17:20:04 +0200410 $columns .= $this->_process_indexes($table);
Andrey Andreeva287a342012-11-05 23:19:59 +0200411 }
412
413 // _create_table will usually have the following format: "%s %s (%s\n)"
Andrey Andreevaaca5cb2014-03-31 17:20:55 +0300414 $sql = sprintf($this->_create_table.'%s',
Andrey Andreeva287a342012-11-05 23:19:59 +0200415 $sql,
416 $this->db->escape_identifiers($table),
Andrey Andreev27f798b2014-01-20 18:19:13 +0200417 $columns,
418 $this->_create_table_attr($attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200419 );
420
421 return $sql;
422 }
423
424 // --------------------------------------------------------------------
425
426 /**
Andrey Andreev27f798b2014-01-20 18:19:13 +0200427 * CREATE TABLE attributes
428 *
429 * @param array $attributes Associative array of table attributes
430 * @return string
431 */
432 protected function _create_table_attr($attributes)
433 {
434 $sql = '';
435
436 foreach (array_keys($attributes) as $key)
437 {
438 if (is_string($key))
439 {
440 $sql .= ' '.strtoupper($key).' '.$attributes[$key];
441 }
442 }
443
444 return $sql;
445 }
446
447 // --------------------------------------------------------------------
448
449 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000450 * Drop Table
451 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200452 * @param string $table_name Table name
453 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000454 * @return bool
455 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200456 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000457 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100458 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300459 {
460 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
461 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200462
Andrey Andreev441d3532015-07-02 11:34:20 +0300463 if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
Andrey Andreeva287a342012-11-05 23:19:59 +0200464 {
465 return TRUE;
466 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300467
Andrey Andreeva287a342012-11-05 23:19:59 +0200468 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300469
470 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200471 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300472 {
473 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
474 if ($key !== FALSE)
475 {
476 unset($this->db->data_cache['table_names'][$key]);
477 }
478 }
479
Andrey Andreeva287a342012-11-05 23:19:59 +0200480 return $query;
481 }
482
483 // --------------------------------------------------------------------
484
485 /**
486 * Drop Table
487 *
488 * Generates a platform-specific DROP TABLE string
489 *
490 * @param string $table Table name
491 * @param bool $if_exists Whether to add an IF EXISTS condition
Andrey Andreevee9d4282017-06-05 10:44:37 +0300492 * @return mixed (Returns a platform-specific DROP table string, or TRUE to indicate there's nothing to do)
Andrey Andreeva287a342012-11-05 23:19:59 +0200493 */
494 protected function _drop_table($table, $if_exists)
495 {
496 $sql = 'DROP TABLE';
497
498 if ($if_exists)
499 {
500 if ($this->_drop_table_if === FALSE)
501 {
502 if ( ! $this->db->table_exists($table))
503 {
504 return TRUE;
505 }
506 }
507 else
508 {
509 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
510 }
511 }
512
513 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000514 }
515
516 // --------------------------------------------------------------------
517
518 /**
519 * Rename Table
520 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200521 * @param string $table_name Old table name
522 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 * @return bool
524 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000525 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000526 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100527 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000528 {
529 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300530 return FALSE;
531 }
532 elseif ($this->_rename_table === FALSE)
533 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200534 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000535 }
Barry Mienydd671972010-10-04 16:33:58 +0200536
Andrey Andreev5d281762012-06-11 22:05:40 +0300537 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300538 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
539 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
540 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300541
542 if ($result && ! empty($this->db->data_cache['table_names']))
543 {
544 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
545 if ($key !== FALSE)
546 {
547 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
548 }
549 }
550
551 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000552 }
553
554 // --------------------------------------------------------------------
555
556 /**
557 * Column Add
558 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200559 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200560 * @param string $table Table name
561 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200562 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000563 * @return bool
564 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200565 public function add_column($table, $field, $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000566 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200567 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200568 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200569
Andrey Andreev24276a32012-01-08 02:44:38 +0200570 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200571 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200572 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
573 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
574 {
575 $field[$k]['after'] = $_after;
576 }
577
Barry Mienydd671972010-10-04 16:33:58 +0200578 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200579 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000580
Andrey Andreeva287a342012-11-05 23:19:59 +0200581 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
582 $this->_reset();
583 if ($sqls === FALSE)
584 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200585 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200586 }
Barry Mienydd671972010-10-04 16:33:58 +0200587
Andrey Andreeva287a342012-11-05 23:19:59 +0200588 for ($i = 0, $c = count($sqls); $i < $c; $i++)
589 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200590 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000591 {
592 return FALSE;
593 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000594 }
Barry Mienydd671972010-10-04 16:33:58 +0200595
Robin Sowell8a54ef22009-03-04 14:49:53 +0000596 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000597 }
598
599 // --------------------------------------------------------------------
600
601 /**
602 * Column Drop
603 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200604 * @param string $table Table name
605 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000606 * @return bool
607 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200608 public function drop_column($table, $column_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000609 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200610 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
611 if ($sql === FALSE)
612 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200613 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200614 }
615
616 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000617 }
618
619 // --------------------------------------------------------------------
620
621 /**
622 * Column Modify
623 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200624 * @param string $table Table name
625 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000626 * @return bool
627 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200628 public function modify_column($table, $field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000629 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200630 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200631 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200632
Andrey Andreev24276a32012-01-08 02:44:38 +0200633 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000634 {
635 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200636 }
Barry Mienydd671972010-10-04 16:33:58 +0200637
Andrey Andreeva287a342012-11-05 23:19:59 +0200638 if (count($this->fields) === 0)
639 {
640 show_error('Field information is required.');
641 }
Barry Mienydd671972010-10-04 16:33:58 +0200642
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200643 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200644 $this->_reset();
645 if ($sqls === FALSE)
646 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200647 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200648 }
649
650 for ($i = 0, $c = count($sqls); $i < $c; $i++)
651 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200652 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000653 {
654 return FALSE;
655 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000656 }
Barry Mienydd671972010-10-04 16:33:58 +0200657
Robin Sowell8a54ef22009-03-04 14:49:53 +0000658 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000659 }
660
661 // --------------------------------------------------------------------
662
663 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200664 * ALTER TABLE
665 *
666 * @param string $alter_type ALTER type
667 * @param string $table Table name
668 * @param mixed $field Column definition
669 * @return string|string[]
670 */
671 protected function _alter_table($alter_type, $table, $field)
672 {
673 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
674
675 // DROP has everything it needs now.
676 if ($alter_type === 'DROP')
677 {
678 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
679 }
680
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200681 $sql .= ($alter_type === 'ADD')
682 ? 'ADD '
683 : $alter_type.' COLUMN ';
684
Andrey Andreeva287a342012-11-05 23:19:59 +0200685 $sqls = array();
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200686 for ($i = 0, $c = count($field); $i < $c; $i++)
Andrey Andreeva287a342012-11-05 23:19:59 +0200687 {
688 $sqls[] = $sql
689 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
690 }
691
692 return $sqls;
693 }
694
695 // --------------------------------------------------------------------
696
697 /**
698 * Process fields
699 *
700 * @param bool $create_table
701 * @return array
702 */
703 protected function _process_fields($create_table = FALSE)
704 {
705 $fields = array();
706
707 foreach ($this->fields as $key => $attributes)
708 {
709 if (is_int($key) && ! is_array($attributes))
710 {
711 $fields[] = array('_literal' => $attributes);
712 continue;
713 }
714
715 $attributes = array_change_key_case($attributes, CASE_UPPER);
716
717 if ($create_table === TRUE && empty($attributes['TYPE']))
718 {
719 continue;
720 }
721
Andrey Andreev13943042014-03-17 11:50:45 +0200722 isset($attributes['TYPE']) && $this->_attr_type($attributes);
Andrey Andreeva287a342012-11-05 23:19:59 +0200723
724 $field = array(
Andrey Andreev13943042014-03-17 11:50:45 +0200725 'name' => $key,
726 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
727 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
728 'length' => '',
729 'unsigned' => '',
Andrey Andreev5eda36d2018-07-26 20:47:32 +0300730 'null' => NULL,
Andrey Andreev13943042014-03-17 11:50:45 +0200731 'unique' => '',
732 'default' => '',
733 'auto_increment' => '',
734 '_literal' => FALSE
Andrey Andreeva287a342012-11-05 23:19:59 +0200735 );
736
Andrey Andreev13943042014-03-17 11:50:45 +0200737 isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
738
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200739 if ($create_table === FALSE)
740 {
741 if (isset($attributes['AFTER']))
742 {
Andrey Andreev96185a32013-10-28 16:04:02 +0200743 $field['after'] = $attributes['AFTER'];
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200744 }
745 elseif (isset($attributes['FIRST']))
746 {
747 $field['first'] = (bool) $attributes['FIRST'];
748 }
749 }
750
Andrey Andreeva287a342012-11-05 23:19:59 +0200751 $this->_attr_default($attributes, $field);
752
753 if (isset($attributes['NULL']))
754 {
755 if ($attributes['NULL'] === TRUE)
756 {
757 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
758 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200759 else
Andrey Andreeva287a342012-11-05 23:19:59 +0200760 {
761 $field['null'] = ' NOT NULL';
762 }
763 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200764 elseif ($create_table === TRUE)
765 {
766 $field['null'] = ' NOT NULL';
767 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200768
769 $this->_attr_auto_increment($attributes, $field);
770 $this->_attr_unique($attributes, $field);
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700771
772 if (isset($attributes['COMMENT']))
773 {
774 $field['comment'] = $this->db->escape($attributes['COMMENT']);
775 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200776
777 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
778 {
779 switch (strtoupper($attributes['TYPE']))
780 {
781 case 'ENUM':
782 case 'SET':
783 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
784 default:
785 $field['length'] = is_array($attributes['CONSTRAINT'])
Andrey Andreevc5a0af22014-03-10 10:29:43 +0200786 ? '('.implode(',', $attributes['CONSTRAINT']).')'
787 : '('.$attributes['CONSTRAINT'].')';
Andrey Andreeva287a342012-11-05 23:19:59 +0200788 break;
789 }
790 }
791
792 $fields[] = $field;
793 }
794
795 return $fields;
796 }
797
798 // --------------------------------------------------------------------
799
800 /**
801 * Process column
802 *
803 * @param array $field
804 * @return string
805 */
806 protected function _process_column($field)
807 {
808 return $this->db->escape_identifiers($field['name'])
809 .' '.$field['type'].$field['length']
810 .$field['unsigned']
811 .$field['default']
812 .$field['null']
813 .$field['auto_increment']
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700814 .$field['unique'];
Andrey Andreeva287a342012-11-05 23:19:59 +0200815 }
816
817 // --------------------------------------------------------------------
818
819 /**
820 * Field attribute TYPE
821 *
822 * Performs a data type mapping between different databases.
823 *
824 * @param array &$attributes
825 * @return void
826 */
827 protected function _attr_type(&$attributes)
828 {
Claudio Galdioloba6bd222015-01-29 11:43:25 -0500829 // Usually overridden by drivers
Andrey Andreeva287a342012-11-05 23:19:59 +0200830 }
831
832 // --------------------------------------------------------------------
833
834 /**
835 * Field attribute UNSIGNED
836 *
837 * Depending on the _unsigned property value:
838 *
839 * - TRUE will always set $field['unsigned'] to 'UNSIGNED'
840 * - FALSE will always set $field['unsigned'] to ''
841 * - array(TYPE) will set $field['unsigned'] to 'UNSIGNED',
842 * if $attributes['TYPE'] is found in the array
843 * - array(TYPE => UTYPE) will change $field['type'],
844 * from TYPE to UTYPE in case of a match
845 *
846 * @param array &$attributes
847 * @param array &$field
848 * @return void
849 */
850 protected function _attr_unsigned(&$attributes, &$field)
851 {
852 if (empty($attributes['UNSIGNED']) OR $attributes['UNSIGNED'] !== TRUE)
853 {
854 return;
855 }
856
857 // Reset the attribute in order to avoid issues if we do type conversion
858 $attributes['UNSIGNED'] = FALSE;
859
860 if (is_array($this->_unsigned))
861 {
862 foreach (array_keys($this->_unsigned) as $key)
863 {
864 if (is_int($key) && strcasecmp($attributes['TYPE'], $this->_unsigned[$key]) === 0)
865 {
866 $field['unsigned'] = ' UNSIGNED';
867 return;
868 }
869 elseif (is_string($key) && strcasecmp($attributes['TYPE'], $key) === 0)
870 {
871 $field['type'] = $key;
872 return;
873 }
874 }
875
876 return;
877 }
878
879 $field['unsigned'] = ($this->_unsigned === TRUE) ? ' UNSIGNED' : '';
880 }
881
882 // --------------------------------------------------------------------
883
884 /**
885 * Field attribute DEFAULT
886 *
887 * @param array &$attributes
888 * @param array &$field
889 * @return void
890 */
891 protected function _attr_default(&$attributes, &$field)
892 {
893 if ($this->_default === FALSE)
894 {
895 return;
896 }
897
898 if (array_key_exists('DEFAULT', $attributes))
899 {
900 if ($attributes['DEFAULT'] === NULL)
901 {
902 $field['default'] = empty($this->_null) ? '' : $this->_default.$this->_null;
903
904 // Override the NULL attribute if that's our default
Andrey Andreev22ce2762014-08-15 11:59:16 +0300905 $attributes['NULL'] = TRUE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200906 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
907 }
908 else
909 {
910 $field['default'] = $this->_default.$this->db->escape($attributes['DEFAULT']);
911 }
912 }
913 }
914
915 // --------------------------------------------------------------------
916
917 /**
918 * Field attribute UNIQUE
919 *
920 * @param array &$attributes
921 * @param array &$field
922 * @return void
923 */
924 protected function _attr_unique(&$attributes, &$field)
925 {
926 if ( ! empty($attributes['UNIQUE']) && $attributes['UNIQUE'] === TRUE)
927 {
928 $field['unique'] = ' UNIQUE';
929 }
930 }
931
932 // --------------------------------------------------------------------
933
934 /**
935 * Field attribute AUTO_INCREMENT
936 *
937 * @param array &$attributes
938 * @param array &$field
939 * @return void
940 */
941 protected function _attr_auto_increment(&$attributes, &$field)
942 {
943 if ( ! empty($attributes['AUTO_INCREMENT']) && $attributes['AUTO_INCREMENT'] === TRUE && stripos($field['type'], 'int') !== FALSE)
944 {
945 $field['auto_increment'] = ' AUTO_INCREMENT';
946 }
947 }
948
949 // --------------------------------------------------------------------
950
951 /**
952 * Process primary keys
953 *
954 * @param string $table Table name
955 * @return string
956 */
957 protected function _process_primary_keys($table)
958 {
959 $sql = '';
960
961 for ($i = 0, $c = count($this->primary_keys); $i < $c; $i++)
962 {
963 if ( ! isset($this->fields[$this->primary_keys[$i]]))
964 {
965 unset($this->primary_keys[$i]);
966 }
967 }
968
969 if (count($this->primary_keys) > 0)
970 {
971 $sql .= ",\n\tCONSTRAINT ".$this->db->escape_identifiers('pk_'.$table)
972 .' PRIMARY KEY('.implode(', ', $this->db->escape_identifiers($this->primary_keys)).')';
973 }
974
975 return $sql;
976 }
977
978 // --------------------------------------------------------------------
979
980 /**
981 * Process indexes
982 *
Andrey Andreevee9d4282017-06-05 10:44:37 +0300983 * @param string $table Table name
984 * @return string[] list of SQL statements
Andrey Andreeva287a342012-11-05 23:19:59 +0200985 */
Andrey Andreev35451022012-11-25 17:20:04 +0200986 protected function _process_indexes($table)
Andrey Andreeva287a342012-11-05 23:19:59 +0200987 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200988 $sqls = array();
989
990 for ($i = 0, $c = count($this->keys); $i < $c; $i++)
991 {
Andrey Andreev35451022012-11-25 17:20:04 +0200992 if (is_array($this->keys[$i]))
993 {
994 for ($i2 = 0, $c2 = count($this->keys[$i]); $i2 < $c2; $i2++)
995 {
996 if ( ! isset($this->fields[$this->keys[$i][$i2]]))
997 {
998 unset($this->keys[$i][$i2]);
999 continue;
1000 }
1001 }
1002 }
1003 elseif ( ! isset($this->fields[$this->keys[$i]]))
Andrey Andreeva287a342012-11-05 23:19:59 +02001004 {
1005 unset($this->keys[$i]);
1006 continue;
1007 }
1008
1009 is_array($this->keys[$i]) OR $this->keys[$i] = array($this->keys[$i]);
1010
mjnaderid3a6ca22013-12-19 01:35:57 +03301011 $sqls[] = 'CREATE INDEX '.$this->db->escape_identifiers($table.'_'.implode('_', $this->keys[$i]))
1012 .' ON '.$this->db->escape_identifiers($table)
Andrey Andreeva287a342012-11-05 23:19:59 +02001013 .' ('.implode(', ', $this->db->escape_identifiers($this->keys[$i])).');';
1014 }
1015
1016 return $sqls;
1017 }
1018
1019 // --------------------------------------------------------------------
1020
1021 /**
Derek Allard2067d1a2008-11-13 22:59:24 +00001022 * Reset
1023 *
1024 * Resets table creation vars
1025 *
Derek Allard2067d1a2008-11-13 22:59:24 +00001026 * @return void
1027 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +00001028 protected function _reset()
Derek Allard2067d1a2008-11-13 22:59:24 +00001029 {
Andrey Andreev24276a32012-01-08 02:44:38 +02001030 $this->fields = $this->keys = $this->primary_keys = array();
Derek Allard2067d1a2008-11-13 22:59:24 +00001031 }
1032
1033}