blob: 152f8f65184603fa4c7ea4b944857b60ca7ff8e0 [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 Andreev593ce682017-01-03 12:40:32 +0200351 isset($this->db->data_cache['table_names']) && $this->db->data_cache['table_names'][] = $table;
Andrey Andreeva287a342012-11-05 23:19:59 +0200352
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 }
Andrey Andreevfbe4d792017-12-27 19:49:03 +0200385
386 $if_not_exists = FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200387 }
388
389 $sql = ($if_not_exists)
390 ? sprintf($this->_create_table_if, $this->db->escape_identifiers($table))
391 : 'CREATE TABLE';
392
393 $columns = $this->_process_fields(TRUE);
394 for ($i = 0, $c = count($columns); $i < $c; $i++)
395 {
396 $columns[$i] = ($columns[$i]['_literal'] !== FALSE)
397 ? "\n\t".$columns[$i]['_literal']
398 : "\n\t".$this->_process_column($columns[$i]);
399 }
400
401 $columns = implode(',', $columns)
402 .$this->_process_primary_keys($table);
403
404 // Are indexes created from within the CREATE TABLE statement? (e.g. in MySQL)
405 if ($this->_create_table_keys === TRUE)
406 {
Andrey Andreev35451022012-11-25 17:20:04 +0200407 $columns .= $this->_process_indexes($table);
Andrey Andreeva287a342012-11-05 23:19:59 +0200408 }
409
410 // _create_table will usually have the following format: "%s %s (%s\n)"
Andrey Andreevaaca5cb2014-03-31 17:20:55 +0300411 $sql = sprintf($this->_create_table.'%s',
Andrey Andreeva287a342012-11-05 23:19:59 +0200412 $sql,
413 $this->db->escape_identifiers($table),
Andrey Andreev27f798b2014-01-20 18:19:13 +0200414 $columns,
415 $this->_create_table_attr($attributes)
Andrey Andreeva287a342012-11-05 23:19:59 +0200416 );
417
418 return $sql;
419 }
420
421 // --------------------------------------------------------------------
422
423 /**
Andrey Andreev27f798b2014-01-20 18:19:13 +0200424 * CREATE TABLE attributes
425 *
426 * @param array $attributes Associative array of table attributes
427 * @return string
428 */
429 protected function _create_table_attr($attributes)
430 {
431 $sql = '';
432
433 foreach (array_keys($attributes) as $key)
434 {
435 if (is_string($key))
436 {
437 $sql .= ' '.strtoupper($key).' '.$attributes[$key];
438 }
439 }
440
441 return $sql;
442 }
443
444 // --------------------------------------------------------------------
445
446 /**
Derek Allard2067d1a2008-11-13 22:59:24 +0000447 * Drop Table
448 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200449 * @param string $table_name Table name
450 * @param bool $if_exists Whether to add an IF EXISTS condition
Derek Allard2067d1a2008-11-13 22:59:24 +0000451 * @return bool
452 */
Andrey Andreeva287a342012-11-05 23:19:59 +0200453 public function drop_table($table_name, $if_exists = FALSE)
Derek Allard2067d1a2008-11-13 22:59:24 +0000454 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100455 if ($table_name === '')
Andrey Andreevd947eba2012-04-09 14:58:28 +0300456 {
457 return ($this->db->db_debug) ? $this->db->display_error('db_table_name_required') : FALSE;
458 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200459
Andrey Andreev441d3532015-07-02 11:34:20 +0300460 if (($query = $this->_drop_table($this->db->dbprefix.$table_name, $if_exists)) === TRUE)
Andrey Andreeva287a342012-11-05 23:19:59 +0200461 {
462 return TRUE;
463 }
Andrey Andreevd947eba2012-04-09 14:58:28 +0300464
Andrey Andreeva287a342012-11-05 23:19:59 +0200465 $query = $this->db->query($query);
Andrey Andreev5d281762012-06-11 22:05:40 +0300466
467 // Update table list cache
Andrey Andreeva287a342012-11-05 23:19:59 +0200468 if ($query && ! empty($this->db->data_cache['table_names']))
Andrey Andreev5d281762012-06-11 22:05:40 +0300469 {
470 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
471 if ($key !== FALSE)
472 {
473 unset($this->db->data_cache['table_names'][$key]);
474 }
475 }
476
Andrey Andreeva287a342012-11-05 23:19:59 +0200477 return $query;
478 }
479
480 // --------------------------------------------------------------------
481
482 /**
483 * Drop Table
484 *
485 * Generates a platform-specific DROP TABLE string
486 *
487 * @param string $table Table name
488 * @param bool $if_exists Whether to add an IF EXISTS condition
Andrey Andreevee9d4282017-06-05 10:44:37 +0300489 * @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 +0200490 */
491 protected function _drop_table($table, $if_exists)
492 {
493 $sql = 'DROP TABLE';
494
495 if ($if_exists)
496 {
497 if ($this->_drop_table_if === FALSE)
498 {
499 if ( ! $this->db->table_exists($table))
500 {
501 return TRUE;
502 }
503 }
504 else
505 {
506 $sql = sprintf($this->_drop_table_if, $this->db->escape_identifiers($table));
507 }
508 }
509
510 return $sql.' '.$this->db->escape_identifiers($table);
Derek Allard2067d1a2008-11-13 22:59:24 +0000511 }
512
513 // --------------------------------------------------------------------
514
515 /**
516 * Rename Table
517 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200518 * @param string $table_name Old table name
519 * @param string $new_table_name New table name
Derek Allard2067d1a2008-11-13 22:59:24 +0000520 * @return bool
521 */
Phil Sturgeona7de97e2011-12-31 18:41:08 +0000522 public function rename_table($table_name, $new_table_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000523 {
Alex Bilbie48a2baf2012-06-02 11:09:54 +0100524 if ($table_name === '' OR $new_table_name === '')
Derek Allard2067d1a2008-11-13 22:59:24 +0000525 {
526 show_error('A table name is required for that operation.');
Andrey Andreevd947eba2012-04-09 14:58:28 +0300527 return FALSE;
528 }
529 elseif ($this->_rename_table === FALSE)
530 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200531 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000532 }
Barry Mienydd671972010-10-04 16:33:58 +0200533
Andrey Andreev5d281762012-06-11 22:05:40 +0300534 $result = $this->db->query(sprintf($this->_rename_table,
Andrey Andreevd947eba2012-04-09 14:58:28 +0300535 $this->db->escape_identifiers($this->db->dbprefix.$table_name),
536 $this->db->escape_identifiers($this->db->dbprefix.$new_table_name))
537 );
Andrey Andreev5d281762012-06-11 22:05:40 +0300538
539 if ($result && ! empty($this->db->data_cache['table_names']))
540 {
541 $key = array_search(strtolower($this->db->dbprefix.$table_name), array_map('strtolower', $this->db->data_cache['table_names']), TRUE);
542 if ($key !== FALSE)
543 {
544 $this->db->data_cache['table_names'][$key] = $this->db->dbprefix.$new_table_name;
545 }
546 }
547
548 return $result;
Derek Allard2067d1a2008-11-13 22:59:24 +0000549 }
550
551 // --------------------------------------------------------------------
552
553 /**
554 * Column Add
555 *
Andrey Andreevb67277b2012-11-12 12:51:14 +0200556 * @todo Remove deprecated $_after option in 3.1+
Andrey Andreeva287a342012-11-05 23:19:59 +0200557 * @param string $table Table name
558 * @param array $field Column definition
Andrey Andreevb67277b2012-11-12 12:51:14 +0200559 * @param string $_after Column for AFTER clause (deprecated)
Derek Allard2067d1a2008-11-13 22:59:24 +0000560 * @return bool
561 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200562 public function add_column($table, $field, $_after = NULL)
Derek Allard2067d1a2008-11-13 22:59:24 +0000563 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200564 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200565 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200566
Andrey Andreev24276a32012-01-08 02:44:38 +0200567 foreach (array_keys($field) as $k)
Barry Mienydd671972010-10-04 16:33:58 +0200568 {
Andrey Andreevb67277b2012-11-12 12:51:14 +0200569 // Backwards-compatibility work-around for MySQL/CUBRID AFTER clause (remove in 3.1+)
570 if ($_after !== NULL && is_array($field[$k]) && ! isset($field[$k]['after']))
571 {
572 $field[$k]['after'] = $_after;
573 }
574
Barry Mienydd671972010-10-04 16:33:58 +0200575 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200576 }
Robin Sowell8a54ef22009-03-04 14:49:53 +0000577
Andrey Andreeva287a342012-11-05 23:19:59 +0200578 $sqls = $this->_alter_table('ADD', $this->db->dbprefix.$table, $this->_process_fields());
579 $this->_reset();
580 if ($sqls === FALSE)
581 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200582 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200583 }
Barry Mienydd671972010-10-04 16:33:58 +0200584
Andrey Andreeva287a342012-11-05 23:19:59 +0200585 for ($i = 0, $c = count($sqls); $i < $c; $i++)
586 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200587 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000588 {
589 return FALSE;
590 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000591 }
Barry Mienydd671972010-10-04 16:33:58 +0200592
Robin Sowell8a54ef22009-03-04 14:49:53 +0000593 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000594 }
595
596 // --------------------------------------------------------------------
597
598 /**
599 * Column Drop
600 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200601 * @param string $table Table name
602 * @param string $column_name Column name
Derek Allard2067d1a2008-11-13 22:59:24 +0000603 * @return bool
604 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200605 public function drop_column($table, $column_name)
Derek Allard2067d1a2008-11-13 22:59:24 +0000606 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200607 $sql = $this->_alter_table('DROP', $this->db->dbprefix.$table, $column_name);
608 if ($sql === FALSE)
609 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200610 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200611 }
612
613 return $this->db->query($sql);
Derek Allard2067d1a2008-11-13 22:59:24 +0000614 }
615
616 // --------------------------------------------------------------------
617
618 /**
619 * Column Modify
620 *
Andrey Andreeva287a342012-11-05 23:19:59 +0200621 * @param string $table Table name
622 * @param string $field Column definition
Derek Allard2067d1a2008-11-13 22:59:24 +0000623 * @return bool
624 */
Andrey Andreevb9061492014-12-04 16:33:24 +0200625 public function modify_column($table, $field)
Derek Allard2067d1a2008-11-13 22:59:24 +0000626 {
Andrey Andreeva287a342012-11-05 23:19:59 +0200627 // Work-around for literal column definitions
Andrey Andreevb9061492014-12-04 16:33:24 +0200628 is_array($field) OR $field = array($field);
Andrey Andreeva287a342012-11-05 23:19:59 +0200629
Andrey Andreev24276a32012-01-08 02:44:38 +0200630 foreach (array_keys($field) as $k)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000631 {
632 $this->add_field(array($k => $field[$k]));
Andrey Andreeva287a342012-11-05 23:19:59 +0200633 }
Barry Mienydd671972010-10-04 16:33:58 +0200634
Andrey Andreeva287a342012-11-05 23:19:59 +0200635 if (count($this->fields) === 0)
636 {
637 show_error('Field information is required.');
638 }
Barry Mienydd671972010-10-04 16:33:58 +0200639
Andrey Andreev7ade8b72012-11-22 13:12:22 +0200640 $sqls = $this->_alter_table('CHANGE', $this->db->dbprefix.$table, $this->_process_fields());
Andrey Andreeva287a342012-11-05 23:19:59 +0200641 $this->_reset();
642 if ($sqls === FALSE)
643 {
Andrey Andreev8d3afde2012-11-06 12:53:47 +0200644 return ($this->db->db_debug) ? $this->db->display_error('db_unsupported_feature') : FALSE;
Andrey Andreeva287a342012-11-05 23:19:59 +0200645 }
646
647 for ($i = 0, $c = count($sqls); $i < $c; $i++)
648 {
Andrey Andreev137a7422012-11-05 23:46:44 +0200649 if ($this->db->query($sqls[$i]) === FALSE)
Robin Sowell8a54ef22009-03-04 14:49:53 +0000650 {
651 return FALSE;
652 }
Derek Allard2067d1a2008-11-13 22:59:24 +0000653 }
Barry Mienydd671972010-10-04 16:33:58 +0200654
Robin Sowell8a54ef22009-03-04 14:49:53 +0000655 return TRUE;
Derek Allard2067d1a2008-11-13 22:59:24 +0000656 }
657
658 // --------------------------------------------------------------------
659
660 /**
Andrey Andreeva287a342012-11-05 23:19:59 +0200661 * ALTER TABLE
662 *
663 * @param string $alter_type ALTER type
664 * @param string $table Table name
665 * @param mixed $field Column definition
666 * @return string|string[]
667 */
668 protected function _alter_table($alter_type, $table, $field)
669 {
670 $sql = 'ALTER TABLE '.$this->db->escape_identifiers($table).' ';
671
672 // DROP has everything it needs now.
673 if ($alter_type === 'DROP')
674 {
675 return $sql.'DROP COLUMN '.$this->db->escape_identifiers($field);
676 }
677
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200678 $sql .= ($alter_type === 'ADD')
679 ? 'ADD '
680 : $alter_type.' COLUMN ';
681
Andrey Andreeva287a342012-11-05 23:19:59 +0200682 $sqls = array();
Andrey Andreev13f6eab2013-03-15 10:56:55 +0200683 for ($i = 0, $c = count($field); $i < $c; $i++)
Andrey Andreeva287a342012-11-05 23:19:59 +0200684 {
685 $sqls[] = $sql
686 .($field[$i]['_literal'] !== FALSE ? $field[$i]['_literal'] : $this->_process_column($field[$i]));
687 }
688
689 return $sqls;
690 }
691
692 // --------------------------------------------------------------------
693
694 /**
695 * Process fields
696 *
697 * @param bool $create_table
698 * @return array
699 */
700 protected function _process_fields($create_table = FALSE)
701 {
702 $fields = array();
703
704 foreach ($this->fields as $key => $attributes)
705 {
706 if (is_int($key) && ! is_array($attributes))
707 {
708 $fields[] = array('_literal' => $attributes);
709 continue;
710 }
711
712 $attributes = array_change_key_case($attributes, CASE_UPPER);
713
714 if ($create_table === TRUE && empty($attributes['TYPE']))
715 {
716 continue;
717 }
718
Andrey Andreev13943042014-03-17 11:50:45 +0200719 isset($attributes['TYPE']) && $this->_attr_type($attributes);
Andrey Andreeva287a342012-11-05 23:19:59 +0200720
721 $field = array(
Andrey Andreev13943042014-03-17 11:50:45 +0200722 'name' => $key,
723 'new_name' => isset($attributes['NAME']) ? $attributes['NAME'] : NULL,
724 'type' => isset($attributes['TYPE']) ? $attributes['TYPE'] : NULL,
725 'length' => '',
726 'unsigned' => '',
Andrey Andreev5eda36d2018-07-26 20:47:32 +0300727 'null' => NULL,
Andrey Andreev13943042014-03-17 11:50:45 +0200728 'unique' => '',
729 'default' => '',
730 'auto_increment' => '',
731 '_literal' => FALSE
Andrey Andreeva287a342012-11-05 23:19:59 +0200732 );
733
Andrey Andreev13943042014-03-17 11:50:45 +0200734 isset($attributes['TYPE']) && $this->_attr_unsigned($attributes, $field);
735
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200736 if ($create_table === FALSE)
737 {
738 if (isset($attributes['AFTER']))
739 {
Andrey Andreev96185a32013-10-28 16:04:02 +0200740 $field['after'] = $attributes['AFTER'];
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200741 }
742 elseif (isset($attributes['FIRST']))
743 {
744 $field['first'] = (bool) $attributes['FIRST'];
745 }
746 }
747
Andrey Andreeva287a342012-11-05 23:19:59 +0200748 $this->_attr_default($attributes, $field);
749
750 if (isset($attributes['NULL']))
751 {
752 if ($attributes['NULL'] === TRUE)
753 {
754 $field['null'] = empty($this->_null) ? '' : ' '.$this->_null;
755 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200756 else
Andrey Andreeva287a342012-11-05 23:19:59 +0200757 {
758 $field['null'] = ' NOT NULL';
759 }
760 }
Andrey Andreev5d69a6e2013-10-28 15:34:47 +0200761 elseif ($create_table === TRUE)
762 {
763 $field['null'] = ' NOT NULL';
764 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200765
766 $this->_attr_auto_increment($attributes, $field);
767 $this->_attr_unique($attributes, $field);
Zachary Flowere4b10bf2014-11-03 10:42:57 -0700768
769 if (isset($attributes['COMMENT']))
770 {
771 $field['comment'] = $this->db->escape($attributes['COMMENT']);
772 }
Andrey Andreeva287a342012-11-05 23:19:59 +0200773
774 if (isset($attributes['TYPE']) && ! empty($attributes['CONSTRAINT']))
775 {
776 switch (strtoupper($attributes['TYPE']))
777 {
778 case 'ENUM':
779 case 'SET':
780 $attributes['CONSTRAINT'] = $this->db->escape($attributes['CONSTRAINT']);
781 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 *
Andrey Andreevee9d4282017-06-05 10:44:37 +0300980 * @param string $table Table name
981 * @return string[] list of SQL statements
Andrey Andreeva287a342012-11-05 23:19:59 +0200982 */
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}