Merge branch 'w23_MDL-33595_m23_phpunitinit' of git://github.com/skodak/moodle
[moodle.git] / lib / ddl / mysql_sql_generator.php
blob801d7401e0fd20e69354a321b3ede33ce6b2145c
1 <?php
3 // This file is part of Moodle - http://moodle.org/
4 //
5 // Moodle is free software: you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation, either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // Moodle is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
19 /**
20 * Mysql specific SQL code generator.
22 * @package core
23 * @subpackage ddl_generator
24 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
25 * 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
29 defined('MOODLE_INTERNAL') || die();
31 require_once($CFG->libdir.'/ddl/sql_generator.php');
33 /// This class generate SQL code to be used against MySQL
34 /// It extends XMLDBgenerator so everything can be
35 /// overridden as needed to generate correct SQL.
37 class mysql_sql_generator extends sql_generator {
39 /// Only set values that are different from the defaults present in XMLDBgenerator
41 public $quote_string = '`'; // String used to quote names
43 public $default_for_char = ''; // To define the default to set for NOT NULLs CHARs without default (null=do nothing)
45 public $drop_default_value_required = true; //To specify if the generator must use some DEFAULT clause to drop defaults
46 public $drop_default_value = NULL; //The DEFAULT clause required to drop defaults
48 public $primary_key_name = ''; //To force primary key names to one string (null=no force)
50 public $drop_primary_key = 'ALTER TABLE TABLENAME DROP PRIMARY KEY'; // Template to drop PKs
51 // with automatic replace for TABLENAME and KEYNAME
53 public $drop_unique_key = 'ALTER TABLE TABLENAME DROP KEY KEYNAME'; // Template to drop UKs
54 // with automatic replace for TABLENAME and KEYNAME
56 public $drop_foreign_key = 'ALTER TABLE TABLENAME DROP FOREIGN KEY KEYNAME'; // Template to drop FKs
57 // with automatic replace for TABLENAME and KEYNAME
59 public $sequence_extra_code = false; //Does the generator need to add extra code to generate the sequence fields
60 public $sequence_name = 'auto_increment'; //Particular name for inline sequences in this generator
62 public $add_after_clause = true; // Does the generator need to add the after clause for fields
64 public $concat_character = null; //Characters to be used as concatenation operator. If not defined
65 //MySQL CONCAT function will be use
67 public $alter_column_sql = 'ALTER TABLE TABLENAME MODIFY COLUMN COLUMNSPECS'; //The SQL template to alter columns
69 public $drop_index_sql = 'ALTER TABLE TABLENAME DROP INDEX INDEXNAME'; //SQL sentence to drop one index
70 //TABLENAME, INDEXNAME are dynamically replaced
72 public $rename_index_sql = null; //SQL sentence to rename one index (MySQL doesn't support this!)
73 //TABLENAME, OLDINDEXNAME, NEWINDEXNAME are dynamically replaced
75 public $rename_key_sql = null; //SQL sentence to rename one key (MySQL doesn't support this!)
76 //TABLENAME, OLDKEYNAME, NEWKEYNAME are dynamically replaced
78 /**
79 * Reset a sequence to the id field of a table.
80 * @param string $table name of table or xmldb_table object
81 * @return array sql commands to execute
83 public function getResetSequenceSQL($table) {
85 if ($table instanceof xmldb_table) {
86 $tablename = $table->getName();
87 } else {
88 $tablename = $table;
91 // From http://dev.mysql.com/doc/refman/5.0/en/alter-table.html
92 $value = (int)$this->mdb->get_field_sql('SELECT MAX(id) FROM {'.$tablename.'}');
93 $value++;
94 return array("ALTER TABLE $this->prefix$tablename AUTO_INCREMENT = $value");
97 /**
98 * Given one correct xmldb_table, returns the SQL statements
99 * to create it (inside one array)
101 public function getCreateTableSQL($xmldb_table) {
102 // first find out if want some special db engine
103 $engine = null;
104 if (method_exists($this->mdb, 'get_dbengine')) {
105 $engine = $this->mdb->get_dbengine();
108 $sqlarr = parent::getCreateTableSQL($xmldb_table);
110 if (!$engine) {
111 // we rely on database defaults
112 return $sqlarr;
115 // let's inject the engine into SQL
116 foreach ($sqlarr as $i=>$sql) {
117 if (strpos($sql, 'CREATE TABLE ') === 0) {
118 $sqlarr[$i] .= " ENGINE = $engine";
122 return $sqlarr;
126 * Given one correct xmldb_table, returns the SQL statements
127 * to create temporary table (inside one array)
129 public function getCreateTempTableSQL($xmldb_table) {
130 $this->temptables->add_temptable($xmldb_table->getName());
131 $sqlarr = parent::getCreateTableSQL($xmldb_table); // we do not want the engine hack included in create table SQL
132 $sqlarr = preg_replace('/^CREATE TABLE (.*)/s', 'CREATE TEMPORARY TABLE $1', $sqlarr);
133 return $sqlarr;
137 * Given one correct xmldb_table, returns the SQL statements
138 * to drop it (inside one array).
140 * @param xmldb_table $xmldb_table The table to drop.
141 * @return array SQL statement(s) for dropping the specified table.
143 public function getDropTableSQL($xmldb_table) {
144 $sqlarr = parent::getDropTableSQL($xmldb_table);
145 if ($this->temptables->is_temptable($xmldb_table->getName())) {
146 $sqlarr = preg_replace('/^DROP TABLE/', "DROP TEMPORARY TABLE", $sqlarr);
147 $this->temptables->delete_temptable($xmldb_table->getName());
149 return $sqlarr;
153 * Given one XMLDB Type, length and decimals, returns the DB proper SQL type
155 public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
157 switch ($xmldb_type) {
158 case XMLDB_TYPE_INTEGER: // From http://mysql.com/doc/refman/5.0/en/numeric-types.html!
159 if (empty($xmldb_length)) {
160 $xmldb_length = 10;
162 if ($xmldb_length > 9) {
163 $dbtype = 'BIGINT';
164 } else if ($xmldb_length > 6) {
165 $dbtype = 'INT';
166 } else if ($xmldb_length > 4) {
167 $dbtype = 'MEDIUMINT';
168 } else if ($xmldb_length > 2) {
169 $dbtype = 'SMALLINT';
170 } else {
171 $dbtype = 'TINYINT';
173 $dbtype .= '(' . $xmldb_length . ')';
174 break;
175 case XMLDB_TYPE_NUMBER:
176 $dbtype = $this->number_type;
177 if (!empty($xmldb_length)) {
178 $dbtype .= '(' . $xmldb_length;
179 if (!empty($xmldb_decimals)) {
180 $dbtype .= ',' . $xmldb_decimals;
182 $dbtype .= ')';
184 break;
185 case XMLDB_TYPE_FLOAT:
186 $dbtype = 'DOUBLE';
187 if (!empty($xmldb_decimals)) {
188 if ($xmldb_decimals < 6) {
189 $dbtype = 'FLOAT';
192 if (!empty($xmldb_length)) {
193 $dbtype .= '(' . $xmldb_length;
194 if (!empty($xmldb_decimals)) {
195 $dbtype .= ',' . $xmldb_decimals;
196 } else {
197 $dbtype .= ', 0'; // In MySQL, if length is specified, decimals are mandatory for FLOATs
199 $dbtype .= ')';
201 break;
202 case XMLDB_TYPE_CHAR:
203 $dbtype = 'VARCHAR';
204 if (empty($xmldb_length)) {
205 $xmldb_length='255';
207 $dbtype .= '(' . $xmldb_length . ')';
208 break;
209 case XMLDB_TYPE_TEXT:
210 $dbtype = 'LONGTEXT';
211 break;
212 case XMLDB_TYPE_BINARY:
213 $dbtype = 'LONGBLOB';
214 break;
215 case XMLDB_TYPE_DATETIME:
216 $dbtype = 'DATETIME';
218 return $dbtype;
222 * Given one xmldb_table and one xmldb_field, return the SQL statements needed to create its default
223 * (usually invoked from getModifyDefaultSQL()
225 public function getCreateDefaultSQL($xmldb_table, $xmldb_field) {
226 /// Just a wrapper over the getAlterFieldSQL() function for MySQL that
227 /// is capable of handling defaults
228 return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
232 * Given one correct xmldb_field and the new name, returns the SQL statements
233 * to rename it (inside one array)
234 * MySQL is pretty different from the standard to justify this overloading
236 public function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
238 /// Need a clone of xmldb_field to perform the change leaving original unmodified
239 $xmldb_field_clone = clone($xmldb_field);
241 /// Change the name of the field to perform the change
242 $xmldb_field_clone->setName($newname);
244 $fieldsql = $this->getFieldSQL($xmldb_table, $xmldb_field_clone);
246 $sql = 'ALTER TABLE ' . $this->getTableName($xmldb_table) . ' CHANGE ' .
247 $xmldb_field->getName() . ' ' . $fieldsql;
249 return array($sql);
253 * Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop its default
254 * (usually invoked from getModifyDefaultSQL()
256 public function getDropDefaultSQL($xmldb_table, $xmldb_field) {
257 /// Just a wrapper over the getAlterFieldSQL() function for MySQL that
258 /// is capable of handling defaults
259 return $this->getAlterFieldSQL($xmldb_table, $xmldb_field);
263 * Returns the code (in array) needed to add one comment to the table
265 function getCommentSQL ($xmldb_table) {
266 $comment = '';
268 if ($xmldb_table->getComment()) {
269 $comment .= 'ALTER TABLE ' . $this->getTableName($xmldb_table);
270 $comment .= " COMMENT='" . $this->addslashes(substr($xmldb_table->getComment(), 0, 60)) . "'";
272 return array($comment);
276 * Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg)
277 * return if such name is currently in use (true) or no (false)
278 * (invoked from getNameForObject()
280 public function isNameInUse($object_name, $type, $table_name) {
282 /// Calculate the real table name
283 $xmldb_table = new xmldb_table($table_name);
284 $tname = $this->getTableName($xmldb_table);
286 switch($type) {
287 case 'ix':
288 case 'uix':
289 /// First of all, check table exists
290 $metatables = $this->mdb->get_tables();
291 if (isset($metatables[$tname])) {
292 /// Fetch all the indexes in the table
293 if ($indexes = $this->mdb->get_indexes($tname)) {
294 /// Look for existing index in array
295 if (isset($indexes[$object_name])) {
296 return true;
300 break;
302 return false; //No name in use found
307 * Returns an array of reserved words (lowercase) for this DB
309 public static function getReservedWords() {
310 /// This file contains the reserved words for MySQL databases
311 /// from http://dev.mysql.com/doc/refman/6.0/en/reserved-words.html
312 $reserved_words = array (
313 'accessible', 'add', 'all', 'alter', 'analyze', 'and', 'as', 'asc',
314 'asensitive', 'before', 'between', 'bigint', 'binary',
315 'blob', 'both', 'by', 'call', 'cascade', 'case', 'change',
316 'char', 'character', 'check', 'collate', 'column',
317 'condition', 'connection', 'constraint', 'continue',
318 'convert', 'create', 'cross', 'current_date', 'current_time',
319 'current_timestamp', 'current_user', 'cursor', 'database',
320 'databases', 'day_hour', 'day_microsecond',
321 'day_minute', 'day_second', 'dec', 'decimal', 'declare',
322 'default', 'delayed', 'delete', 'desc', 'describe',
323 'deterministic', 'distinct', 'distinctrow', 'div', 'double',
324 'drop', 'dual', 'each', 'else', 'elseif', 'enclosed', 'escaped',
325 'exists', 'exit', 'explain', 'false', 'fetch', 'float', 'float4',
326 'float8', 'for', 'force', 'foreign', 'from', 'fulltext', 'grant',
327 'group', 'having', 'high_priority', 'hour_microsecond',
328 'hour_minute', 'hour_second', 'if', 'ignore', 'in', 'index',
329 'infile', 'inner', 'inout', 'insensitive', 'insert', 'int', 'int1',
330 'int2', 'int3', 'int4', 'int8', 'integer', 'interval', 'into', 'is',
331 'iterate', 'join', 'key', 'keys', 'kill', 'leading', 'leave', 'left',
332 'like', 'limit', 'linear', 'lines', 'load', 'localtime', 'localtimestamp',
333 'lock', 'long', 'longblob', 'longtext', 'loop', 'low_priority', 'master_heartbeat_period',
334 'master_ssl_verify_server_cert', 'match', 'mediumblob', 'mediumint', 'mediumtext',
335 'middleint', 'minute_microsecond', 'minute_second',
336 'mod', 'modifies', 'natural', 'not', 'no_write_to_binlog',
337 'null', 'numeric', 'on', 'optimize', 'option', 'optionally',
338 'or', 'order', 'out', 'outer', 'outfile', 'overwrite', 'precision', 'primary',
339 'procedure', 'purge', 'raid0', 'range', 'read', 'read_only', 'read_write', 'reads', 'real',
340 'references', 'regexp', 'release', 'rename', 'repeat', 'replace',
341 'require', 'restrict', 'return', 'revoke', 'right', 'rlike', 'schema',
342 'schemas', 'second_microsecond', 'select', 'sensitive',
343 'separator', 'set', 'show', 'smallint', 'soname', 'spatial',
344 'specific', 'sql', 'sqlexception', 'sqlstate', 'sqlwarning',
345 'sql_big_result', 'sql_calc_found_rows', 'sql_small_result',
346 'ssl', 'starting', 'straight_join', 'table', 'terminated', 'then',
347 'tinyblob', 'tinyint', 'tinytext', 'to', 'trailing', 'trigger', 'true',
348 'undo', 'union', 'unique', 'unlock', 'unsigned', 'update',
349 'upgrade', 'usage', 'use', 'using', 'utc_date', 'utc_time',
350 'utc_timestamp', 'values', 'varbinary', 'varchar', 'varcharacter',
351 'varying', 'when', 'where', 'while', 'with', 'write', 'x509',
352 'xor', 'year_month', 'zerofill'
354 return $reserved_words;