"MDL-12304, fix double text"
[moodle-linuxchix.git] / lib / xmldb / classes / generators / XMLDBGenerator.class.php
blob55822491a3cdb36d6c16dc55fa50fa139a8848ee
1 <?php // $Id$
3 ///////////////////////////////////////////////////////////////////////////
4 // //
5 // NOTICE OF COPYRIGHT //
6 // //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment //
8 // http://moodle.com //
9 // //
10 // Copyright (C) 1999 onwards Martin Dougiamas http://dougiamas.com //
11 // (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
12 // //
13 // This program is free software; you can redistribute it and/or modify //
14 // it under the terms of the GNU General Public License as published by //
15 // the Free Software Foundation; either version 2 of the License, or //
16 // (at your option) any later version. //
17 // //
18 // This program is distributed in the hope that it will be useful, //
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of //
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
21 // GNU General Public License for more details: //
22 // //
23 // http://www.gnu.org/copyleft/gpl.html //
24 // //
25 ///////////////////////////////////////////////////////////////////////////
27 /// This class represent the base generator class where all the
28 /// needed functions to generate proper SQL are defined.
30 /// The rest of classes will inherit, by default, the same logic.
31 /// Functions will be overriden as needed to generate correct SQL.
33 class XMLDBgenerator {
35 /// Please, avoid editing this defaults in this base class!
36 /// It could change the behaviour of the rest of generators
37 /// that, by default, inherit this configuration.
38 /// To change any of them, do it in extended classes instead.
40 var $quote_string = '"'; // String used to quote names
42 var $quote_all = false; // To decide if we want to quote all the names or only the reserved ones
44 var $statement_end = ';'; // String to be automatically added at the end of each statement
46 var $integer_to_number = false; // To create all the integers as NUMBER(x) (also called DECIMAL, NUMERIC...)
47 var $float_to_number = false; // To create all the floats as NUMBER(x) (also called DECIMAL, NUMERIC...)
49 var $number_type = 'NUMERIC'; // Proper type for NUMBER(x) in this DB
51 var $unsigned_allowed = true; // To define in the generator must handle unsigned information
52 var $default_for_char = null; // To define the default to set for NOT NULLs CHARs without default (null=do nothing)
54 var $drop_default_clause_required = false; //To specify if the generator must use some DEFAULT clause to drop defaults
55 var $drop_default_clause = ''; //The DEFAULT clause required to drop defaults
57 var $default_after_null = true; //To decide if the default clause of each field must go after the null clause
59 var $specify_nulls = false; //To force the generator if NULL clauses must be specified. It shouldn't be necessary
60 //but some mssql drivers require them or everything is created as NOT NULL :-(
62 var $primary_key_name = null; //To force primary key names to one string (null=no force)
64 var $primary_keys = true; // Does the generator build primary keys
65 var $unique_keys = false; // Does the generator build unique keys
66 var $foreign_keys = false; // Does the generator build foreign keys
68 var $drop_primary_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME'; // Template to drop PKs
69 // with automatic replace for TABLENAME and KEYNAME
71 var $drop_unique_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME'; // Template to drop UKs
72 // with automatic replace for TABLENAME and KEYNAME
74 var $drop_foreign_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME'; // Template to drop FKs
75 // with automatic replace for TABLENAME and KEYNAME
77 var $sequence_extra_code = true; //Does the generator need to add extra code to generate the sequence fields
78 var $sequence_name = 'auto_increment'; //Particular name for inline sequences in this generator
79 var $sequence_name_small = false; //Different name for small (4byte) sequences or false if same
80 var $sequence_only = false; //To avoid to output the rest of the field specs, leaving only the name and the sequence_name variable
82 var $enum_inline_code = true; //Does the generator need to add inline code in the column definition
83 var $enum_extra_code = true; //Does the generator need to add extra code to generate code for the enums in the table
85 var $add_table_comments = true; // Does the generator need to add code for table comments
87 var $add_after_clause = false; // Does the generator need to add the after clause for fields
89 var $prefix_on_names = true; //Does the generator need to prepend the prefix to all the key/index/sequence/trigger/check names
91 var $names_max_length = 30; //Max length for key/index/sequence/trigger/check names (keep 30 for all!)
93 var $concat_character = '||'; //Characters to be used as concatenation operator. If not defined
94 //MySQL CONCAT function will be used
96 var $rename_table_sql = 'ALTER TABLE OLDNAME RENAME TO NEWNAME'; //SQL sentence to rename one table, both
97 //OLDNAME and NEWNAME are dinamically replaced
99 var $rename_table_extra_code = false; //Does the generator need to add code after table rename
101 var $drop_table_sql = 'DROP TABLE TABLENAME'; //SQL sentence to drop one table
102 //TABLENAME is dinamically replaced
104 var $drop_table_extra_code = false; //Does the generator need to add code after table drop
106 var $alter_column_sql = 'ALTER TABLE TABLENAME ALTER COLUMN COLUMNSPECS'; //The SQL template to alter columns
108 var $alter_column_skip_default = false; //The generator will skip the default clause on alter columns
110 var $alter_column_skip_type = false; //The generator will skip the type clause on alter columns
112 var $alter_column_skip_notnull = false; //The generator will skip the null/notnull clause on alter columns
114 var $rename_column_sql = 'ALTER TABLE TABLENAME RENAME COLUMN OLDFIELDNAME TO NEWFIELDNAME';
115 ///TABLENAME, OLDFIELDNAME and NEWFIELDNAME are dianmically replaced
117 var $rename_column_extra_code = false; //Does the generator need to add code after column rename
119 var $drop_index_sql = 'DROP INDEX INDEXNAME'; //SQL sentence to drop one index
120 //TABLENAME, INDEXNAME are dinamically replaced
122 var $rename_index_sql = 'ALTER INDEX OLDINDEXNAME RENAME TO NEWINDEXNAME'; //SQL sentence to rename one index
123 //TABLENAME, OLDINDEXNAME, NEWINDEXNAME are dinamically replaced
125 var $rename_key_sql = 'ALTER TABLE TABLENAME CONSTRAINT OLDKEYNAME RENAME TO NEWKEYNAME'; //SQL sentence to rename one key
126 //TABLENAME, OLDKEYNAME, NEWKEYNAME are dinamically replaced
128 var $prefix; // Prefix to be used for all the DB objects
130 var $reserved_words; // List of reserved words (in order to quote them properly)
133 * Creates one new XMLDBGenerator
135 function XMLDBgenerator() {
136 global $CFG;
137 $this->prefix = '';
138 $this->reserved_words = $this->getReservedWords();
141 /// ALL THESE FUNCTION ARE SHARED BY ALL THE XMLDGenerator classes
144 * Set the prefix
146 function setPrefix($prefix) {
147 if ($this->prefix_on_names) { // Only if we want prefix on names
148 $this->prefix = $prefix;
153 * Given one XMLDBTable, returns it's correct name, depending of all the parametrization
155 * @param XMLDBTable table whose name we want
156 * @param boolean to specify if the name must be quoted (if reserved word, only!)
157 * @return string the correct name of the table
159 function getTableName($xmldb_table, $quoted = true) {
161 $prefixtouse = $this->prefix;
162 /// Determinate if this table must have prefix or no
163 if (in_array($xmldb_table->getName(), $this->getTablesWithoutPrefix())) {
164 $prefixtouse = '';
166 /// Get the name
167 $tablename = $prefixtouse . $xmldb_table->getName();
168 /// Apply quotes conditionally
169 if ($quoted) {
170 $tablename = $this->getEncQuoted($tablename);
173 return $tablename;
177 * Given one correct XMLDBTable, returns the SQL statements
178 * to create it (inside one array)
180 function getCreateTableSQL($xmldb_table) {
182 $results = array(); //Array where all the sentences will be stored
184 /// Table header
185 $table = 'CREATE TABLE ' . $this->getTableName($xmldb_table) . ' (';
187 if (!$xmldb_fields = $xmldb_table->getFields()) {
188 return $results;
191 /// Prevent tables without prefix to be duplicated (part of MDL-6614)
192 if (in_array($xmldb_table->getName(), $this->getTablesWithoutPrefix()) &&
193 table_exists($xmldb_table)) {
194 return $results; // false here would break the install, empty array is better ;-)
197 /// Add the fields, separated by commas
198 foreach ($xmldb_fields as $xmldb_field) {
199 $table .= "\n " . $this->getFieldSQL($xmldb_field);
200 $table .= ',';
202 /// Add the keys, separated by commas
203 if ($xmldb_keys = $xmldb_table->getKeys()) {
204 foreach ($xmldb_keys as $xmldb_key) {
205 if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
206 $table .= "\nCONSTRAINT " . $keytext . ',';
208 /// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
209 if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
210 ///Duplicate the key
211 $xmldb_key->setType(XMLDB_KEY_UNIQUE);
212 if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
213 $table .= "\nCONSTRAINT " . $keytext . ',';
218 /// Add enum extra code if needed
219 if ($this->enum_extra_code) {
220 /// Iterate over fields looking for enums
221 foreach ($xmldb_fields as $xmldb_field) {
222 if ($xmldb_field->getEnum()) {
223 $table .= "\n" . $this->getEnumExtraSQL($xmldb_table, $xmldb_field) . ',';
227 /// Table footer, trim the latest comma
228 $table = trim($table,',');
229 $table .= "\n)";
231 /// Add the CREATE TABLE to results
232 $results[] = $table;
234 /// Add comments if specified and it exists
235 if ($this->add_table_comments && $xmldb_table->getComment()) {
236 $comment = $this->getCommentSQL ($xmldb_table);
237 /// Add the COMMENT to results
238 $results = array_merge($results, $comment);
241 /// Add the indexes (each one, one statement)
242 if ($xmldb_indexes = $xmldb_table->getIndexes()) {
243 foreach ($xmldb_indexes as $xmldb_index) {
244 ///Only process all this if the index doesn't exist in DB
245 if (!index_exists($xmldb_table, $xmldb_index)) {
246 if ($indextext = $this->getCreateIndexSQL($xmldb_table, $xmldb_index)) {
247 $results = array_merge($results, $indextext);
253 /// Also, add the indexes needed from keys, based on configuration (each one, one statement)
254 if ($xmldb_keys = $xmldb_table->getKeys()) {
255 foreach ($xmldb_keys as $xmldb_key) {
256 /// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
257 /// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
258 if (!$this->getKeySQL($xmldb_table, $xmldb_key) || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
259 /// Create the interim index
260 $index = new XMLDBIndex('anyname');
261 $index->setFields($xmldb_key->getFields());
262 ///Only process all this if the index doesn't exist in DB
263 if (!index_exists($xmldb_table, $index)) {
264 $createindex = false; //By default
265 switch ($xmldb_key->getType()) {
266 case XMLDB_KEY_UNIQUE:
267 case XMLDB_KEY_FOREIGN_UNIQUE:
268 $index->setUnique(true);
269 $createindex = true;
270 break;
271 case XMLDB_KEY_FOREIGN:
272 $index->setUnique(false);
273 $createindex = true;
274 break;
276 if ($createindex) {
277 if ($indextext = $this->getCreateIndexSQL($xmldb_table, $index)) {
278 /// Add the INDEX to the array
279 $results = array_merge($results, $indextext);
287 /// Add sequence extra code if needed
288 if ($this->sequence_extra_code) {
289 /// Iterate over fields looking for sequences
290 foreach ($xmldb_fields as $xmldb_field) {
291 if ($xmldb_field->getSequence()) {
292 /// returns an array of statements needed to create one sequence
293 $sequence_sentences = $this->getCreateSequenceSQL($xmldb_table, $xmldb_field);
294 /// Add the SEQUENCE to the array
295 $results = array_merge($results, $sequence_sentences);
300 return $results;
304 * Given one correct XMLDBIndex, returns the SQL statements
305 * needed to create it (in array)
307 function getCreateIndexSQL ($xmldb_table, $xmldb_index) {
309 $unique = '';
310 $suffix = 'ix';
311 if ($xmldb_index->getUnique()) {
312 $unique = ' UNIQUE';
313 $suffix = 'uix';
316 $index = 'CREATE' . $unique . ' INDEX ';
317 $index .= $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_index->getFields()), $suffix);
318 $index .= ' ON ' . $this->getTableName($xmldb_table);
319 $index .= ' (' . implode(', ', $this->getEncQuoted($xmldb_index->getFields())) . ')';
321 return array($index);
325 * Given one correct XMLDBField, returns the complete SQL line to create it
327 function getFieldSQL($xmldb_field, $skip_type_clause = false, $skip_default_clause = false, $skip_notnull_clause = false) {
329 /// First of all, convert integers to numbers if defined
330 if ($this->integer_to_number) {
331 if ($xmldb_field->getType() == XMLDB_TYPE_INTEGER) {
332 $xmldb_field->setType(XMLDB_TYPE_NUMBER);
335 /// Same for floats
336 if ($this->float_to_number) {
337 if ($xmldb_field->getType() == XMLDB_TYPE_FLOAT) {
338 $xmldb_field->setType(XMLDB_TYPE_NUMBER);
342 /// The name
343 $field = $this->getEncQuoted($xmldb_field->getName());
344 /// The type and length only if we don't want to skip it
345 if (!$skip_type_clause) {
346 /// The type and length (if the field isn't enum)
347 if (!$xmldb_field->getEnum() || $this->enum_inline_code == false) {
348 $field .= ' ' . $this->getTypeSQL($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
349 } else {
350 /// call to custom function
351 $field .= ' ' . $this->getEnumSQL($xmldb_field);
354 /// The unsigned if supported
355 if ($this->unsigned_allowed && ($xmldb_field->getType() == XMLDB_TYPE_INTEGER ||
356 $xmldb_field->getType() == XMLDB_TYPE_NUMBER ||
357 $xmldb_field->getType() == XMLDB_TYPE_FLOAT)) {
358 if ($xmldb_field->getUnsigned()) {
359 $field .= ' unsigned';
362 /// Calculate the not null clause
363 $notnull = '';
364 /// Only if we don't want to skip it
365 if (!$skip_notnull_clause) {
366 if ($xmldb_field->getNotNull()) {
367 $notnull = ' NOT NULL';
368 } else {
369 if ($this->specify_nulls) {
370 $notnull = ' NULL';
374 /// Calculate the default clause
375 if (!$skip_default_clause) { //Only if we don't want to skip it
376 $default = $this->getDefaultClause($xmldb_field);
377 } else {
378 $default = '';
380 /// Based on default_after_null, set both clauses properly
381 if ($this->default_after_null) {
382 $field .= $notnull . $default;
383 } else {
384 $field .= $default . $notnull;
386 /// The sequence
387 if ($xmldb_field->getSequence()) {
388 if($xmldb_field->getLength()<=9 && $this->sequence_name_small) {
389 $sequencename=$this->sequence_name_small;
390 } else {
391 $sequencename=$this->sequence_name;
393 $field .= ' ' . $sequencename;
394 if ($this->sequence_only) {
395 /// We only want the field name and sequence name to be printed
396 /// so, calculate it and return
397 return $this->getEncQuoted($xmldb_field->getName()) . ' ' . $sequencename;
400 return $field;
404 * Given one correct XMLDBKey, returns its specs
406 function getKeySQL ($xmldb_table, $xmldb_key) {
408 $key = '';
410 switch ($xmldb_key->getType()) {
411 case XMLDB_KEY_PRIMARY:
412 if ($this->primary_keys) {
413 if ($this->primary_key_name !== null) {
414 $key = $this->getEncQuoted($this->primary_key_name);
415 } else {
416 $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'pk');
418 $key .= ' PRIMARY KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
420 break;
421 case XMLDB_KEY_UNIQUE:
422 if ($this->unique_keys) {
423 $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'uk');
424 $key .= ' UNIQUE (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
426 break;
427 case XMLDB_KEY_FOREIGN:
428 case XMLDB_KEY_FOREIGN_UNIQUE:
429 if ($this->foreign_keys) {
430 $key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'fk');
431 $key .= ' FOREIGN KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
432 $key .= ' REFERENCES ' . $this->getEncQuoted($this->prefix . $xmldb_key->getRefTable());
433 $key .= ' (' . implode(', ', $this->getEncQuoted($xmldb_key->getRefFields())) . ')';
435 break;
438 return $key;
442 * Give one XMLDBField, returns the correct "default value" for the current configuration
444 function getDefaultValue ($xmldb_field) {
446 $default = null;
448 if ($xmldb_field->getDefault() !== NULL) {
449 if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
450 $xmldb_field->getType() == XMLDB_TYPE_TEXT) {
451 $default = "'" . addslashes($xmldb_field->getDefault()) . "'";
452 } else {
453 $default = $xmldb_field->getDefault();
455 } else {
456 /// We force default '' for not null char columns without proper default
457 /// some day this should be out!
458 if ($this->default_for_char !== NULL &&
459 $xmldb_field->getType() == XMLDB_TYPE_CHAR &&
460 $xmldb_field->getNotNull()) {
461 $default = "'" . $this->default_for_char . "'";
462 } else {
463 /// If the DB requires to explicity define some clause to drop one default, do it here
464 /// never applying defaults to TEXT and BINARY fields
465 if ($this->drop_default_clause_required &&
466 $xmldb_field->getType() != XMLDB_TYPE_TEXT &&
467 $xmldb_field->getType() != XMLDB_TYPE_BINARY && !$xmldb_field->getNotNull()) {
468 $default = $this->drop_default_clause;
472 return $default;
476 * Given one XMLDBField, returns the correct "default clause" for the current configuration
478 function getDefaultClause ($xmldb_field) {
480 $defaultvalue = $this->getDefaultValue ($xmldb_field);
482 if ($defaultvalue !== null) {
483 return ' DEFAULT ' . $defaultvalue;
484 } else {
485 return null;
490 * Given one correct XMLDBTable and the new name, returns the SQL statements
491 * to rename it (inside one array)
493 function getRenameTableSQL($xmldb_table, $newname) {
495 $results = array(); //Array where all the sentences will be stored
497 $newt = new XMLDBTable($newname); //Temporal table for name calculations
499 $rename = str_replace('OLDNAME', $this->getTableName($xmldb_table), $this->rename_table_sql);
500 $rename = str_replace('NEWNAME', $this->getTableName($newt), $rename);
502 $results[] = $rename;
504 /// Call to getRenameTableExtraSQL() if $rename_table_extra_code is enabled. It will add sequence regeneration code.
505 if ($this->rename_table_extra_code) {
506 $extra_sentences = $this->getRenameTableExtraSQL($xmldb_table, $newname);
507 $results = array_merge($results, $extra_sentences);
510 return $results;
514 * Given one correct XMLDBTable and the new name, returns the SQL statements
515 * to drop it (inside one array)
517 function getDropTableSQL($xmldb_table) {
519 $results = array(); //Array where all the sentences will be stored
521 $drop = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_table_sql);
523 $results[] = $drop;
525 /// call to getDropTableExtraSQL() if $drop_table_extra_code is enabled. It will add sequence/trigger drop code.
526 if ($this->drop_table_extra_code) {
527 $extra_sentences = $this->getDropTableExtraSQL($xmldb_table);
528 $results = array_merge($results, $extra_sentences);
531 return $results;
535 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to add the field to the table
537 function getAddFieldSQL($xmldb_table, $xmldb_field) {
539 $results = array();
541 /// Get the quoted name of the table and field
542 $tablename = $this->getTableName($xmldb_table);
544 /// Build the standard alter table add
545 $altertable = 'ALTER TABLE ' . $tablename . ' ADD ' .
546 $this->getFieldSQL($xmldb_field, $this->alter_column_skip_type,
547 $this->alter_column_skip_default,
548 $this->alter_column_skip_notnull);
549 /// Add the after clause if necesary
550 if ($this->add_after_clause && $xmldb_field->getPrevious()) {
551 $altertable .= ' after ' . $this->getEncQuoted($xmldb_field->getPrevious());
553 $results[] = $altertable;
555 /// If the DB has extra enum code
556 if ($this->enum_extra_code) {
557 /// If it's enum add the extra code
558 if ($xmldb_field->getEnum()) {
559 $results[] = 'ALTER TABLE ' . $tablename . ' ADD ' . $this->getEnumExtraSQL($xmldb_table, $xmldb_field);
563 return $results;
567 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to drop the field from the table
569 function getDropFieldSQL($xmldb_table, $xmldb_field) {
571 $results = array();
573 /// Get the quoted name of the table and field
574 $tablename = $this->getTableName($xmldb_table);
575 $fieldname = $this->getEncQuoted($xmldb_field->getName());
577 /// Build the standard alter table drop
578 $results[] = 'ALTER TABLE ' . $tablename . ' DROP COLUMN ' . $fieldname;
580 return $results;
584 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to alter the field in the table
586 function getAlterFieldSQL($xmldb_table, $xmldb_field) {
588 $results = array();
590 /// Always specify NULLs in alter fields because we can change not nulls to nulls
591 $this->specify_nulls = true;
593 /// Get the quoted name of the table and field
594 $tablename = $this->getTableName($xmldb_table);
595 $fieldname = $this->getEncQuoted($xmldb_field->getName());
597 /// Build de alter sentence using the alter_column_sql template
598 $alter = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->alter_column_sql);
599 $alter = str_replace('COLUMNSPECS', $this->getFieldSQL($xmldb_field, $this->alter_column_skip_type,
600 $this->alter_column_skip_default,
601 $this->alter_column_skip_notnull), $alter);
603 /// Add the after clause if necesary
604 if ($this->add_after_clause && $xmldb_field->getPrevious()) {
605 $alter .= ' after ' . $this->getEncQuoted($xmldb_field->getPrevious());
608 /// Build the standard alter table modify
609 $results[] = $alter;
611 return $results;
615 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to modify the enum of the field in the table
617 function getModifyEnumSQL($xmldb_table, $xmldb_field) {
619 $results = array();
621 /// Get the quoted name of the table and field
622 $tablename = $this->getTableName($xmldb_table);
623 $fieldname = $this->getEncQuoted($xmldb_field->getName());
625 /// Decide if we are going to create or to drop the enum (based exclusively in the values passed!)
626 if (!$xmldb_field->getEnum()) {
627 $results = $this->getDropEnumSQL($xmldb_table, $xmldb_field); //Drop
628 } else {
629 $results = $this->getCreateEnumSQL($xmldb_table, $xmldb_field); //Create/modify
632 return $results;
636 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to modify the default of the field in the table
638 function getModifyDefaultSQL($xmldb_table, $xmldb_field) {
640 $results = array();
642 /// Get the quoted name of the table and field
643 $tablename = $this->getTableName($xmldb_table);
644 $fieldname = $this->getEncQuoted($xmldb_field->getName());
646 /// Decide if we are going to create/modify or to drop the default
647 if ($xmldb_field->getDefault() === null) {
648 $results = $this->getDropDefaultSQL($xmldb_table, $xmldb_field); //Drop
649 } else {
650 $results = $this->getCreateDefaultSQL($xmldb_table, $xmldb_field); //Create/modify
653 return $results;
657 * Given one correct XMLDBField and the new name, returns the SQL statements
658 * to rename it (inside one array)
660 function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
662 $results = array(); //Array where all the sentences will be stored
664 /// Although this is checked in ddllib - rename_field() - double check
665 /// that we aren't trying to rename one "id" field. Although it could be
666 /// implemented (if adding the necessary code to rename sequences, defaults,
667 /// triggers... and so on under each getRenameFieldExtraSQL() function, it's
668 /// better to forbide it, mainly because this field is the default PK and
669 /// in the future, a lot of FKs can be pointing here. So, this field, more
670 /// or less, must be considered inmutable!
671 if ($xmldb_field->getName() == 'id') {
672 return array();
675 $rename = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_column_sql);
676 $rename = str_replace('OLDFIELDNAME', $this->getEncQuoted($xmldb_field->getName()), $rename);
677 $rename = str_replace('NEWFIELDNAME', $this->getEncQuoted($newname), $rename);
679 $results[] = $rename;
681 /// Call to getRenameFieldExtraSQL() if $rename_column_extra_code is enabled (will add some required sentences)
682 if ($this->rename_column_extra_code) {
683 $extra_sentences = $this->getRenameFieldExtraSQL($xmldb_table, $xmldb_field, $newname);
684 $results = array_merge($results, $extra_sentences);
687 return $results;
691 * Given one XMLDBTable and one XMLDBKey, return the SQL statements needded to add the key to the table
692 * note that undelying indexes will be added as parametrised by $xxxx_keys and $xxxx_index parameters
694 function getAddKeySQL($xmldb_table, $xmldb_key) {
696 $results = array();
698 /// Just use the CreateKeySQL function
699 if ($keyclause = $this->getKeySQL($xmldb_table, $xmldb_key)) {
700 $key = 'ALTER TABLE ' . $this->getTableName($xmldb_table) .
701 ' ADD CONSTRAINT ' . $keyclause;
702 $results[] = $key;
705 /// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
706 /// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
707 if (!$keyclause || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
708 /// Only if they don't exist
709 if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN) { ///Calculate type of index based on type ok key
710 $indextype = XMLDB_INDEX_NOTUNIQUE;
711 } else {
712 $indextype = XMLDB_INDEX_UNIQUE;
714 $xmldb_index = new XMLDBIndex('anyname');
715 $xmldb_index->setAttributes($indextype, $xmldb_key->getFields());
716 if (!index_exists($xmldb_table, $xmldb_index)) {
717 $results = array_merge($results, $this->getAddIndexSQL($xmldb_table, $xmldb_index));
721 /// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
722 if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
723 ///Duplicate the key
724 $xmldb_key->setType(XMLDB_KEY_UNIQUE);
725 $results = array_merge($results, $this->getAddKeySQL($xmldb_table, $xmldb_key));
728 /// Return results
729 return $results;
733 * Given one XMLDBTable and one XMLDBIndex, return the SQL statements needded to drop the index from the table
735 function getDropKeySQL($xmldb_table, $xmldb_key) {
737 $results = array();
739 /// Get the key name (note that this doesn't introspect DB, so could cause some problems sometimes!)
740 /// TODO: We'll need to overwrite the whole getDropKeySQL() method inside each DB to do the proper queries
741 /// against the dictionary or require ADOdb to support it or change the find_key_name() method to
742 /// perform DB introspection directly. But, for now, as we aren't going to enable referential integrity
743 /// it won't be a problem at all
744 $dbkeyname = find_key_name($xmldb_table, $xmldb_key);
746 /// Only if such type of key generation is enabled
747 $dropkey = false;
748 switch ($xmldb_key->getType()) {
749 case XMLDB_KEY_PRIMARY:
750 if ($this->primary_keys) {
751 $template = $this->drop_primary_key;
752 $dropkey = true;
754 break;
755 case XMLDB_KEY_UNIQUE:
756 if ($this->unique_keys) {
757 $template = $this->drop_unique_key;
758 $dropkey = true;
760 break;
761 case XMLDB_KEY_FOREIGN_UNIQUE:
762 case XMLDB_KEY_FOREIGN:
763 if ($this->foreign_keys) {
764 $template = $this->drop_foreign_key;
765 $dropkey = true;
767 break;
769 /// If we have decided to drop the key, let's do it
770 if ($dropkey) {
771 /// Replace TABLENAME, CONSTRAINTTYPE and KEYNAME as needed
772 $dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $template);
773 $dropsql = str_replace('KEYNAME', $dbkeyname, $dropsql);
775 $results[] = $dropsql;
778 /// If we aren't dropping the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
779 /// automatically by the RDBMS) drop the underlying (created by us) index (if exists)
780 if (!$dropkey || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
781 /// Only if they exist
782 $xmldb_index = new XMLDBIndex('anyname');
783 $xmldb_index->setAttributes(XMLDB_INDEX_UNIQUE, $xmldb_key->getFields());
784 if (index_exists($xmldb_table, $xmldb_index)) {
785 $results = array_merge($results, $this->getDropIndexSQL($xmldb_table, $xmldb_index));
789 /// If the key is XMLDB_KEY_FOREIGN_UNIQUE, drop the UNIQUE too
790 if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
791 ///Duplicate the key
792 $xmldb_key->setType(XMLDB_KEY_UNIQUE);
793 $results = array_merge($results, $this->getDropKeySQL($xmldb_table, $xmldb_key));
796 /// Return results
797 return $results;
801 * Given one XMLDBTable and one XMLDBKey, return the SQL statements needded to rename the key in the table
802 * Experimental! Shouldn't be used at all!
805 function getRenameKeySQL($xmldb_table, $xmldb_key, $newname) {
807 $results = array();
809 /// Get the real key name
810 $dbkeyname = find_key_name($xmldb_table, $xmldb_key);
812 /// Check we are really generating this type of keys
813 if (($xmldb_key->getType() == XMLDB_KEY_PRIMARY && !$this->primary_keys) ||
814 ($xmldb_key->getType() == XMLDB_KEY_UNIQUE && !$this->unique_keys) ||
815 ($xmldb_key->getType() == XMLDB_KEY_FOREIGN && !$this->foreign_keys) ||
816 ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && !$this->unique_keys && !$this->foreign_keys)) {
817 /// We aren't generating this type of keys, delegate to child indexes
818 $xmldb_index = new XMLDBIndex($xmldb_key->getName());
819 $xmldb_index->setFields($xmldb_key->getFields());
820 return $this->getRenameIndexSQL($xmldb_table, $xmldb_index, $newname);
823 /// Arrived here so we are working with keys, lets rename them
824 /// Replace TABLENAME and KEYNAME as needed
825 $renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_key_sql);
826 $renamesql = str_replace('OLDKEYNAME', $dbkeyname, $renamesql);
827 $renamesql = str_replace('NEWKEYNAME', $newname, $renamesql);
829 /// Some DB doesn't support key renaming so this can be empty
830 if ($renamesql) {
831 $results[] = $renamesql;
834 return $results;
838 * Given one XMLDBTable and one XMLDBIndex, return the SQL statements needded to add the index to the table
840 function getAddIndexSQL($xmldb_table, $xmldb_index) {
842 /// Just use the CreateIndexSQL function
843 return $this->getCreateIndexSQL($xmldb_table, $xmldb_index);
847 * Given one XMLDBTable and one XMLDBIndex, return the SQL statements needded to drop the index from the table
849 function getDropIndexSQL($xmldb_table, $xmldb_index) {
851 $results = array();
853 /// Get the real index name
854 $dbindexname = find_index_name($xmldb_table, $xmldb_index);
856 /// Replace TABLENAME and INDEXNAME as needed
857 $dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
858 $dropsql = str_replace('INDEXNAME', $dbindexname, $dropsql);
860 $results[] = $dropsql;
862 return $results;
866 * Given one XMLDBTable and one XMLDBIndex, return the SQL statements needded to rename the index in the table
867 * Experimental! Shouldn't be used at all!
870 function getRenameIndexSQL($xmldb_table, $xmldb_index, $newname) {
872 $results = array();
874 /// Get the real index name
875 $dbindexname = find_index_name($xmldb_table, $xmldb_index);
877 /// Replace TABLENAME and INDEXNAME as needed
878 $renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_index_sql);
879 $renamesql = str_replace('OLDINDEXNAME', $dbindexname, $renamesql);
880 $renamesql = str_replace('NEWINDEXNAME', $newname, $renamesql);
882 /// Some DB doesn't support index renaming (MySQL) so this can be empty
883 if ($renamesql) {
884 $results[] = $renamesql;
887 return $results;
891 * Given three strings (table name, list of fields (comma separated) and suffix),
892 * create the proper object name quoting it if necessary.
894 * IMPORTANT: This function must be used to CALCULATE NAMES of objects TO BE CREATED,
895 * NEVER TO GUESS NAMES of EXISTING objects!!!
897 function getNameForObject($tablename, $fields, $suffix='') {
899 $name = '';
901 /// Implement one basic cache to avoid object name duplication
902 /// and to speed up repeated queries for the same objects
903 if (!isset($used_names)) {
904 static $used_names = array();
907 /// If this exact object has been requested, return it
908 if (array_key_exists($tablename.'-'.$fields.'-'.$suffix, $used_names)) {
909 return $used_names[$tablename.'-'.$fields.'-'.$suffix];
912 /// Use standard naming. See http://docs.moodle.org/en/XMLDB_key_and_index_naming
913 $tablearr = explode ('_', $tablename);
914 foreach ($tablearr as $table) {
915 $name .= substr(trim($table),0,4);
917 $name .= '_';
918 $fieldsarr = explode (',', $fields);
919 foreach ($fieldsarr as $field) {
920 $name .= substr(trim($field),0,3);
922 /// Prepend the prefix
923 $name = $this->prefix . $name;
925 $name = substr(trim($name), 0, $this->names_max_length - 1 - strlen($suffix)); //Max names_max_length
927 /// Add the suffix
928 $namewithsuffix = $name;
929 if ($suffix) {
930 $namewithsuffix = $namewithsuffix . '_' . $suffix;
933 /// If the calculated name is in the cache, or if we detect it by introspecting the DB let's modify if
934 if (in_array($namewithsuffix, $used_names) || $this->isNameInUse($namewithsuffix, $suffix, $tablename)) {
935 $counter = 2;
936 /// If have free space, we add 2
937 if (strlen($namewithsuffix) < $this->names_max_length) {
938 $newname = $name . $counter;
939 /// Else replace the last char by 2
940 } else {
941 $newname = substr($name, 0, strlen($name)-1) . $counter;
943 $newnamewithsuffix = $newname;
944 if ($suffix) {
945 $newnamewithsuffix = $newnamewithsuffix . '_' . $suffix;
947 /// Now iterate until not used name is found, incrementing the counter
948 while (in_array($newnamewithsuffix, $used_names) || $this->isNameInUse($newnamewithsuffix, $suffix, $tablename)) {
949 $counter++;
950 $newname = substr($name, 0, strlen($newname)-1) . $counter;
951 $newnamewithsuffix = $newname;
952 if ($suffix) {
953 $newnamewithsuffix = $newnamewithsuffix . '_' . $suffix;
956 $namewithsuffix = $newnamewithsuffix;
959 /// Add the name to the cache
960 $used_names[$tablename.'-'.$fields.'-'.$suffix] = $namewithsuffix;
962 /// Quote it if necessary (reserved words)
963 $namewithsuffix = $this->getEncQuoted($namewithsuffix);
965 return $namewithsuffix;
969 * Given any string (or one array), enclose it by the proper quotes
970 * if it's a reserved word
972 function getEncQuoted($input) {
974 if (is_array($input)) {
975 foreach ($input as $key=>$content) {
976 $input[$key] = $this->getEncQuoted($content);
978 return $input;
979 } else {
980 /// Always lowercase
981 $input = strtolower($input);
982 /// if reserved or quote_all, quote it
983 if ($this->quote_all || in_array($input, $this->reserved_words)) {
984 $input = $this->quote_string . $input . $this->quote_string;
986 return $input;
991 * Given one XMLDB Statement, build the needed SQL insert sentences to execute it
993 function getExecuteInsertSQL($statement) {
995 $results = array(); //Array where all the sentences will be stored
997 if ($sentences = $statement->getSentences()) {
998 foreach ($sentences as $sentence) {
999 /// Get the list of fields
1000 $fields = $statement->getFieldsFromInsertSentence($sentence);
1001 /// Get the values of fields
1002 $values = $statement->getValuesFromInsertSentence($sentence);
1003 /// Look if we have some CONCAT value and transform it dinamically
1004 foreach($values as $key => $value) {
1005 /// Trim single quotes
1006 $value = trim($value,"'");
1007 if (stristr($value, 'CONCAT') !== false){
1008 /// Look for data between parentesis
1009 preg_match("/CONCAT\s*\((.*)\)$/is", trim($value), $matches);
1010 if (isset($matches[1])) {
1011 $part = $matches[1];
1012 /// Convert the comma separated string to an array
1013 $arr = XMLDBObject::comma2array($part);
1014 if ($arr) {
1015 $value = $this->getConcatSQL($arr);
1019 /// Values to be sent to DB must be properly escaped
1020 $value = addslashes($value);
1021 /// Back trimmed quotes
1022 $value = "'" . $value . "'";
1023 /// Back to the array
1024 $values[$key] = $value;
1027 /// Iterate over fields, escaping them if necessary
1028 foreach($fields as $key => $field) {
1029 $fields[$key] = $this->getEncQuoted($field);
1031 /// Build the final SQL sentence and add it to the array of results
1032 $sql = 'INSERT INTO ' . $this->getEncQuoted($this->prefix . $statement->getTable()) .
1033 '(' . implode(', ', $fields) . ') ' .
1034 'VALUES (' . implode(', ', $values) . ')';
1035 $results[] = $sql;
1039 return $results;
1043 * Given one array of elements, build de proper CONCAT expresion, based
1044 * in the $concat_character setting. If such setting is empty, then
1045 * MySQL's CONCAT function will be used instead
1047 function getConcatSQL($elements) {
1049 /// Replace double quoted elements by single quotes
1050 foreach($elements as $key => $element) {
1051 $element = trim($element);
1052 if (substr($element, 0, 1) == '"' &&
1053 substr($element, -1, 1) == '"') {
1054 $elements[$key] = "'" . trim($element, '"') . "'";
1058 /// Now call the standard sql_concat() DML function
1059 return call_user_func_array('sql_concat', $elements);
1063 * Given one string (or one array), ends it with statement_end
1065 function getEndedStatements ($input) {
1067 if (is_array($input)) {
1068 foreach ($input as $key=>$content) {
1069 $input[$key] = $this->getEndedStatements($content);
1071 return $input;
1072 } else {
1073 $input = trim($input) . $this->statement_end;
1074 return $input;
1079 * Returns the name (string) of the sequence used in the table for the autonumeric pk
1080 * Only some DB have this implemented
1082 function getSequenceFromDB($xmldb_table) {
1083 return false;
1087 * Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg)
1088 * return if such name is currently in use (true) or no (false)
1089 * (MySQL requires the whole XMLDBTable object to be specified, so we add it always)
1090 * (invoked from getNameForObject()
1091 * Only some DB have this implemented
1093 function isNameInUse($object_name, $type, $table_name) {
1094 return false; //For generators not implementing introspecion,
1095 //we always return with the name being free to be used
1099 /// ALL THESE FUNCTION MUST BE CUSTOMISED BY ALL THE XMLDGenerator classes
1102 * Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
1104 function getTypeSQL ($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
1105 return 'code for type(precision) goes to function getTypeSQL()';
1109 * Given one XMLDB Field, return its enum SQL to be added inline with the column definition
1111 function getEnumSQL ($xmldb_field) {
1112 return 'code for inline enum declaration goes to function getEnumSQL(). Can be disabled with enum_inline_code=false';
1116 * Returns the code needed to create one enum for the xmldb_table and xmldb_field passes
1118 function getEnumExtraSQL ($xmldb_table, $xmldb_field) {
1119 return 'Code for extra enum SQL goes to getEnumExtraSQL(). Can be disabled with enum_extra_code=false';
1123 * Returns the code (array of statements) needed to execute extra statements on field rename
1125 function getRenameFieldExtraSQL ($xmldb_table, $xmldb_field) {
1126 return array('Code for field rename goes to getRenameFieldExtraSQL(). Can be disabled with rename_column_extra_code=false;');
1130 * Returns the code (array of statements) needed
1131 * to create one sequence for the xmldb_table and xmldb_field passes
1133 function getCreateSequenceSQL ($xmldb_table, $xmldb_field) {
1134 return array('Code for extra sequence SQL goes to getCreateSequenceSQL(). Can be disabled with sequence_extra_code=false');
1138 * Returns the code (array of statements) needed to add one comment to the table
1140 function getCommentSQL ($xmldb_table) {
1141 return array('Code for table comment goes to getCommentSQL(). Can be disabled with add_table_comments=false;');
1145 * Returns the code (array of statements) needed to execute extra statements on table rename
1147 function getRenameTableExtraSQL ($xmldb_table) {
1148 return array('Code for table rename goes to getRenameTableExtraSQL(). Can be disabled with rename_table_extra_code=false;');
1152 * Returns the code (array of statements) needed to execute extra statements on table drop
1154 function getDropTableExtraSQL ($xmldb_table) {
1155 return array('Code for table drop goes to getDropTableExtraSQL(). Can be disabled with drop_table_extra_code=false;');
1159 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to drop its enum
1160 * (usually invoked from getModifyEnumSQL()
1162 function getDropEnumSQL($xmldb_table, $xmldb_field) {
1163 return array('Code to drop one enum goes to getDropEnumSQL()');
1167 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to add its enum
1168 * (usually invoked from getModifyEnumSQL()
1170 function getCreateEnumSQL($xmldb_table, $xmldb_field) {
1171 return array('Code to create one enum goes to getCreateEnumSQL()');
1175 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to drop its default
1176 * (usually invoked from getModifyDefaultSQL()
1178 function getDropDefaultSQL($xmldb_table, $xmldb_field) {
1179 return array('Code to drop one default goes to getDropDefaultSQL()');
1183 * Given one XMLDBTable and one optional XMLDBField, return one array with all the check
1184 * constrainst found for that table (or field). Must exist for each DB supported.
1185 * (usually invoked from find_check_constraint_name)
1187 function getCheckConstraintsFromDB($xmldb_table, $xmldb_field=null) {
1188 return array('Code to fetch check constraints goes to getCheckConstraintsFromDB()');
1192 * Given one XMLDBTable and one XMLDBField, return the SQL statements needded to add its default
1193 * (usually invoked from getModifyDefaultSQL()
1195 function getCreateDefaultSQL($xmldb_table, $xmldb_field) {
1196 return array('Code to create one default goes to getCreateDefaultSQL()');
1200 * Returns an array of reserved words (lowercase) for this DB
1201 * You MUST provide the real list for each DB inside every XMLDB class
1203 function getReservedWords() {
1204 /// Some well-know reserved words
1205 $reserved_words = array (
1206 'user', 'scale', 'type', 'comment', 'view', 'value', 'table', 'index', 'key', 'sequence', 'trigger'
1208 return $reserved_words;
1212 * Returns an array of tables to be built without prefix (lowercase)
1213 * It's enough to keep updated here this function.
1215 function getTablesWithoutPrefix() {
1216 /// Some well-known tables to be created without prefix
1217 $tables = array (
1218 'adodb_logsql'
1220 return $tables;