mirror of
https://github.com/moodle/moodle.git
synced 2025-01-18 05:58:34 +01:00
29a541724f
This has been generated running the following Sniff, part of the Moodle's CodeSniffer standard: - PSR2.Methods.MethodDeclaration It just ensures all the function declarations have the correct order for: - abstract and final. - visibility (public, protected, private). - static. So, all the lines modified by this commit are function declarations and the only changes are in the positions of those keywords.
1463 lines
61 KiB
PHP
1463 lines
61 KiB
PHP
<?php
|
|
// This file is part of Moodle - http://moodle.org/
|
|
//
|
|
// Moodle is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Moodle is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/**
|
|
* This class represent the base generator class where all the needed functions to generate proper SQL are defined.
|
|
*
|
|
* The rest of classes will inherit, by default, the same logic.
|
|
* Functions will be overridden as needed to generate correct SQL.
|
|
*
|
|
* @package core_ddl
|
|
* @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
|
|
* 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
|
|
defined('MOODLE_INTERNAL') || die();
|
|
|
|
/**
|
|
* Abstract sql generator class, base for all db specific implementations.
|
|
*
|
|
* @package core_ddl
|
|
* @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
|
|
* 2001-3001 Eloy Lafuente (stronk7) http://contiento.com
|
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
*/
|
|
abstract class sql_generator {
|
|
|
|
// Please, avoid editing this defaults in this base class!
|
|
// It could change the behaviour of the rest of generators
|
|
// that, by default, inherit this configuration.
|
|
// To change any of them, do it in extended classes instead.
|
|
|
|
/** @var string Used to quote names. */
|
|
public $quote_string = '"';
|
|
|
|
/** @var string To be automatically added at the end of each statement. */
|
|
public $statement_end = ';';
|
|
|
|
/** @var bool To decide if we want to quote all the names or only the reserved ones. */
|
|
public $quote_all = false;
|
|
|
|
/** @var bool To create all the integers as NUMBER(x) (also called DECIMAL, NUMERIC...). */
|
|
public $integer_to_number = false;
|
|
|
|
/** @var bool To create all the floats as NUMBER(x) (also called DECIMAL, NUMERIC...). */
|
|
public $float_to_number = false;
|
|
|
|
/** @var string Proper type for NUMBER(x) in this DB. */
|
|
public $number_type = 'NUMERIC';
|
|
|
|
/** @var string To define the default to set for NOT NULLs CHARs without default (null=do nothing).*/
|
|
public $default_for_char = null;
|
|
|
|
/** @var bool To specify if the generator must use some DEFAULT clause to drop defaults.*/
|
|
public $drop_default_value_required = false;
|
|
|
|
/** @var string The DEFAULT clause required to drop defaults.*/
|
|
public $drop_default_value = '';
|
|
|
|
/** @var bool To decide if the default clause of each field must go after the null clause.*/
|
|
public $default_after_null = true;
|
|
|
|
/** @var bool To force the generator if NULL clauses must be specified. It shouldn't be necessary.*/
|
|
public $specify_nulls = false;
|
|
|
|
/** @var string To force primary key names to one string (null=no force).*/
|
|
public $primary_key_name = null;
|
|
|
|
/** @var bool True if the generator builds primary keys.*/
|
|
public $primary_keys = true;
|
|
|
|
/** @var bool True if the generator builds unique keys.*/
|
|
public $unique_keys = false;
|
|
|
|
/** @var bool True if the generator builds foreign keys.*/
|
|
public $foreign_keys = false;
|
|
|
|
/** @var string Template to drop PKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
public $drop_primary_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
|
/** @var string Template to drop UKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
public $drop_unique_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
|
/** @var string Template to drop FKs. 'TABLENAME' and 'KEYNAME' will be replaced from this template.*/
|
|
public $drop_foreign_key = 'ALTER TABLE TABLENAME DROP CONSTRAINT KEYNAME';
|
|
|
|
/** @var bool True if the generator needs to add extra code to generate the sequence fields.*/
|
|
public $sequence_extra_code = true;
|
|
|
|
/** @var string The particular name for inline sequences in this generator.*/
|
|
public $sequence_name = 'auto_increment';
|
|
|
|
/** @var string|bool Different name for small (4byte) sequences or false if same.*/
|
|
public $sequence_name_small = false;
|
|
|
|
/**
|
|
* @var bool To avoid outputting the rest of the field specs, leaving only the name and the sequence_name returned.
|
|
* @see getFieldSQL()
|
|
*/
|
|
public $sequence_only = false;
|
|
|
|
/** @var bool True if the generator needs to add code for table comments.*/
|
|
public $add_table_comments = true;
|
|
|
|
/** @var bool True if the generator needs to add the after clause for fields.*/
|
|
public $add_after_clause = false;
|
|
|
|
/**
|
|
* @var bool True if the generator needs to prepend the prefix to all the key/index/sequence/trigger/check names.
|
|
* @see $prefix
|
|
*/
|
|
public $prefix_on_names = true;
|
|
|
|
/** @var int Maximum length for key/index/sequence/trigger/check names (keep 30 for all!).*/
|
|
public $names_max_length = 30;
|
|
|
|
/** @var string Characters to be used as concatenation operator. If not defined, MySQL CONCAT function will be used.*/
|
|
public $concat_character = '||';
|
|
|
|
/** @var string SQL sentence to rename one table, both 'OLDNAME' and 'NEWNAME' keywords are dynamically replaced.*/
|
|
public $rename_table_sql = 'ALTER TABLE OLDNAME RENAME TO NEWNAME';
|
|
|
|
/** @var string SQL sentence to drop one table where the 'TABLENAME' keyword is dynamically replaced.*/
|
|
public $drop_table_sql = 'DROP TABLE TABLENAME';
|
|
|
|
/** @var string The SQL template to alter columns where the 'TABLENAME' and 'COLUMNSPECS' keywords are dynamically replaced.*/
|
|
public $alter_column_sql = 'ALTER TABLE TABLENAME ALTER COLUMN COLUMNSPECS';
|
|
|
|
/** @var bool The generator will skip the default clause on alter columns.*/
|
|
public $alter_column_skip_default = false;
|
|
|
|
/** @var bool The generator will skip the type clause on alter columns.*/
|
|
public $alter_column_skip_type = false;
|
|
|
|
/** @var bool The generator will skip the null/notnull clause on alter columns.*/
|
|
public $alter_column_skip_notnull = false;
|
|
|
|
/** @var string SQL sentence to rename one column where 'TABLENAME', 'OLDFIELDNAME' and 'NEWFIELDNAME' keywords are dynamically replaced.*/
|
|
public $rename_column_sql = 'ALTER TABLE TABLENAME RENAME COLUMN OLDFIELDNAME TO NEWFIELDNAME';
|
|
|
|
/** @var string SQL sentence to drop one index where 'TABLENAME', 'INDEXNAME' keywords are dynamically replaced.*/
|
|
public $drop_index_sql = 'DROP INDEX INDEXNAME';
|
|
|
|
/** @var string SQL sentence to rename one index where 'TABLENAME', 'OLDINDEXNAME' and 'NEWINDEXNAME' are dynamically replaced.*/
|
|
public $rename_index_sql = 'ALTER INDEX OLDINDEXNAME RENAME TO NEWINDEXNAME';
|
|
|
|
/** @var string SQL sentence to rename one key 'TABLENAME', 'OLDKEYNAME' and 'NEWKEYNAME' are dynamically replaced.*/
|
|
public $rename_key_sql = 'ALTER TABLE TABLENAME CONSTRAINT OLDKEYNAME RENAME TO NEWKEYNAME';
|
|
|
|
/** @var string The prefix to be used for all the DB objects.*/
|
|
public $prefix;
|
|
|
|
/** @var array List of reserved words (in order to quote them properly).*/
|
|
public $reserved_words;
|
|
|
|
/** @var moodle_database The moodle_database instance.*/
|
|
public $mdb;
|
|
|
|
/** @var Control existing temptables.*/
|
|
protected $temptables;
|
|
|
|
/**
|
|
* Creates a new sql_generator.
|
|
* @param moodle_database $mdb The moodle_database object instance.
|
|
* @param moodle_temptables $temptables The optional moodle_temptables instance, null by default.
|
|
*/
|
|
public function __construct($mdb, $temptables = null) {
|
|
$this->prefix = $mdb->get_prefix();
|
|
$this->reserved_words = $this->getReservedWords();
|
|
$this->mdb = $mdb; // this creates circular reference - the other link must be unset when closing db
|
|
$this->temptables = $temptables;
|
|
}
|
|
|
|
/**
|
|
* Releases all resources.
|
|
*/
|
|
public function dispose() {
|
|
$this->mdb = null;
|
|
}
|
|
|
|
/**
|
|
* Given one string (or one array), ends it with $statement_end .
|
|
*
|
|
* @see $statement_end
|
|
*
|
|
* @param array|string $input SQL statement(s).
|
|
* @return array|string
|
|
*/
|
|
public function getEndedStatements($input) {
|
|
|
|
if (is_array($input)) {
|
|
foreach ($input as $key=>$content) {
|
|
$input[$key] = $this->getEndedStatements($content);
|
|
}
|
|
return $input;
|
|
} else {
|
|
$input = trim($input).$this->statement_end;
|
|
return $input;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table, checks if it exists in DB (true/false).
|
|
*
|
|
* @param mixed $table The table to be searched (string name or xmldb_table instance).
|
|
* @return boolean true/false
|
|
*/
|
|
public function table_exists($table) {
|
|
if (is_string($table)) {
|
|
$tablename = $table;
|
|
} else {
|
|
// Calculate the name of the table
|
|
$tablename = $table->getName();
|
|
}
|
|
|
|
if ($this->temptables->is_temptable($tablename)) {
|
|
return true;
|
|
}
|
|
|
|
// Get all tables in moodle database.
|
|
$tables = $this->mdb->get_tables();
|
|
return isset($tables[$tablename]);
|
|
}
|
|
|
|
/**
|
|
* This function will return the SQL code needed to create db tables and statements.
|
|
* @see xmldb_structure
|
|
*
|
|
* @param xmldb_structure $xmldb_structure An xmldb_structure instance.
|
|
* @return array
|
|
*/
|
|
public function getCreateStructureSQL($xmldb_structure) {
|
|
$results = array();
|
|
|
|
if ($tables = $xmldb_structure->getTables()) {
|
|
foreach ($tables as $table) {
|
|
$results = array_merge($results, $this->getCreateTableSQL($table));
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table, this returns it's correct name, depending of all the parameterization.
|
|
* eg: This appends $prefix to the table name.
|
|
*
|
|
* @see $prefix
|
|
*
|
|
* @param xmldb_table $xmldb_table The table whose name we want.
|
|
* @param boolean $quoted To specify if the name must be quoted (if reserved word, only!).
|
|
* @return string The correct name of the table.
|
|
*/
|
|
public function getTableName(xmldb_table $xmldb_table, $quoted=true) {
|
|
// Get the name
|
|
$tablename = $this->prefix.$xmldb_table->getName();
|
|
|
|
// Apply quotes optionally
|
|
if ($quoted) {
|
|
$tablename = $this->getEncQuoted($tablename);
|
|
}
|
|
|
|
return $tablename;
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_table, returns the SQL statements
|
|
* to create it (inside one array).
|
|
*
|
|
* @param xmldb_table $xmldb_table An xmldb_table instance.
|
|
* @return array An array of SQL statements, starting with the table creation SQL followed
|
|
* by any of its comments, indexes and sequence creation SQL statements.
|
|
*/
|
|
public function getCreateTableSQL($xmldb_table) {
|
|
if ($error = $xmldb_table->validateDefinition()) {
|
|
throw new coding_exception($error);
|
|
}
|
|
|
|
$results = array(); //Array where all the sentences will be stored
|
|
|
|
// Table header
|
|
$table = 'CREATE TABLE ' . $this->getTableName($xmldb_table) . ' (';
|
|
|
|
if (!$xmldb_fields = $xmldb_table->getFields()) {
|
|
return $results;
|
|
}
|
|
|
|
$sequencefield = null;
|
|
|
|
// Add the fields, separated by commas
|
|
foreach ($xmldb_fields as $xmldb_field) {
|
|
if ($xmldb_field->getSequence()) {
|
|
$sequencefield = $xmldb_field->getName();
|
|
}
|
|
$table .= "\n " . $this->getFieldSQL($xmldb_table, $xmldb_field);
|
|
$table .= ',';
|
|
}
|
|
// Add the keys, separated by commas
|
|
if ($xmldb_keys = $xmldb_table->getKeys()) {
|
|
foreach ($xmldb_keys as $xmldb_key) {
|
|
if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
$table .= "\nCONSTRAINT " . $keytext . ',';
|
|
}
|
|
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
|
|
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
|
|
//Duplicate the key
|
|
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
if ($keytext = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
$table .= "\nCONSTRAINT " . $keytext . ',';
|
|
}
|
|
}
|
|
// make sure sequence field is unique
|
|
if ($sequencefield and $xmldb_key->getType() == XMLDB_KEY_PRIMARY) {
|
|
$fields = $xmldb_key->getFields();
|
|
$field = reset($fields);
|
|
if ($sequencefield === $field) {
|
|
$sequencefield = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// throw error if sequence field does not have unique key defined
|
|
if ($sequencefield) {
|
|
throw new ddl_exception('ddsequenceerror', $xmldb_table->getName());
|
|
}
|
|
|
|
// Table footer, trim the latest comma
|
|
$table = trim($table,',');
|
|
$table .= "\n)";
|
|
|
|
// Add the CREATE TABLE to results
|
|
$results[] = $table;
|
|
|
|
// Add comments if specified and it exists
|
|
if ($this->add_table_comments && $xmldb_table->getComment()) {
|
|
$comment = $this->getCommentSQL($xmldb_table);
|
|
// Add the COMMENT to results
|
|
$results = array_merge($results, $comment);
|
|
}
|
|
|
|
// Add the indexes (each one, one statement)
|
|
if ($xmldb_indexes = $xmldb_table->getIndexes()) {
|
|
foreach ($xmldb_indexes as $xmldb_index) {
|
|
//tables do not exist yet, which means indexed can not exist yet
|
|
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $xmldb_index)) {
|
|
$results = array_merge($results, $indextext);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Also, add the indexes needed from keys, based on configuration (each one, one statement)
|
|
if ($xmldb_keys = $xmldb_table->getKeys()) {
|
|
foreach ($xmldb_keys as $xmldb_key) {
|
|
// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
|
|
if (!$this->getKeySQL($xmldb_table, $xmldb_key) || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
// Create the interim index
|
|
$index = new xmldb_index('anyname');
|
|
$index->setFields($xmldb_key->getFields());
|
|
//tables do not exist yet, which means indexed can not exist yet
|
|
$createindex = false; //By default
|
|
switch ($xmldb_key->getType()) {
|
|
case XMLDB_KEY_UNIQUE:
|
|
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
$index->setUnique(true);
|
|
$createindex = true;
|
|
break;
|
|
case XMLDB_KEY_FOREIGN:
|
|
$index->setUnique(false);
|
|
$createindex = true;
|
|
break;
|
|
}
|
|
if ($createindex) {
|
|
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $index)) {
|
|
// Add the INDEX to the array
|
|
$results = array_merge($results, $indextext);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add sequence extra code if needed
|
|
if ($this->sequence_extra_code) {
|
|
// Iterate over fields looking for sequences
|
|
foreach ($xmldb_fields as $xmldb_field) {
|
|
if ($xmldb_field->getSequence()) {
|
|
// returns an array of statements needed to create one sequence
|
|
$sequence_sentences = $this->getCreateSequenceSQL($xmldb_table, $xmldb_field);
|
|
// Add the SEQUENCE to the array
|
|
$results = array_merge($results, $sequence_sentences);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_index, returns the SQL statements
|
|
* needed to create it (in array).
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table instance to create the index on.
|
|
* @param xmldb_index $xmldb_index The xmldb_index to create.
|
|
* @return array An array of SQL statements to create the index.
|
|
* @throws coding_exception Thrown if the xmldb_index does not validate with the xmldb_table.
|
|
*/
|
|
public function getCreateIndexSQL($xmldb_table, $xmldb_index) {
|
|
if ($error = $xmldb_index->validateDefinition($xmldb_table)) {
|
|
throw new coding_exception($error);
|
|
}
|
|
|
|
$unique = '';
|
|
$suffix = 'ix';
|
|
if ($xmldb_index->getUnique()) {
|
|
$unique = ' UNIQUE';
|
|
$suffix = 'uix';
|
|
}
|
|
|
|
$index = 'CREATE' . $unique . ' INDEX ';
|
|
$index .= $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_index->getFields()), $suffix);
|
|
$index .= ' ON ' . $this->getTableName($xmldb_table);
|
|
$index .= ' (' . implode(', ', $this->getEncQuoted($xmldb_index->getFields())) . ')';
|
|
|
|
return array($index);
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_field, returns the complete SQL line to create it.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
* @param string $specify_nulls_clause To force a specific null clause, NULL by default.
|
|
* @param bool $specify_field_name Flag to specify fieldname in return.
|
|
* @return string The field generating SQL statement.
|
|
* @throws coding_exception Thrown when xmldb_field doesn't validate with the xmldb_table.
|
|
*/
|
|
public function getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL, $specify_nulls_clause = NULL, $specify_field_name = true) {
|
|
if ($error = $xmldb_field->validateDefinition($xmldb_table)) {
|
|
throw new coding_exception($error);
|
|
}
|
|
|
|
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
$specify_nulls_clause = is_null($specify_nulls_clause) ? $this->specify_nulls : $specify_nulls_clause;
|
|
|
|
// First of all, convert integers to numbers if defined
|
|
if ($this->integer_to_number) {
|
|
if ($xmldb_field->getType() == XMLDB_TYPE_INTEGER) {
|
|
$xmldb_field->setType(XMLDB_TYPE_NUMBER);
|
|
}
|
|
}
|
|
// Same for floats
|
|
if ($this->float_to_number) {
|
|
if ($xmldb_field->getType() == XMLDB_TYPE_FLOAT) {
|
|
$xmldb_field->setType(XMLDB_TYPE_NUMBER);
|
|
}
|
|
}
|
|
|
|
$field = ''; // Let's accumulate the whole expression based on params and settings
|
|
// The name
|
|
if ($specify_field_name) {
|
|
$field .= $this->getEncQuoted($xmldb_field->getName());
|
|
}
|
|
// The type and length only if we don't want to skip it
|
|
if (!$skip_type_clause) {
|
|
// The type and length
|
|
$field .= ' ' . $this->getTypeSQL($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
|
|
}
|
|
// note: unsigned is not supported any more since moodle 2.3, all numbers are signed
|
|
// Calculate the not null clause
|
|
$notnull = '';
|
|
// Only if we don't want to skip it
|
|
if (!$skip_notnull_clause) {
|
|
if ($xmldb_field->getNotNull()) {
|
|
$notnull = ' NOT NULL';
|
|
} else {
|
|
if ($specify_nulls_clause) {
|
|
$notnull = ' NULL';
|
|
}
|
|
}
|
|
}
|
|
// Calculate the default clause
|
|
$default_clause = '';
|
|
if (!$skip_default_clause) { //Only if we don't want to skip it
|
|
$default_clause = $this->getDefaultClause($xmldb_field);
|
|
}
|
|
// Based on default_after_null, set both clauses properly
|
|
if ($this->default_after_null) {
|
|
$field .= $notnull . $default_clause;
|
|
} else {
|
|
$field .= $default_clause . $notnull;
|
|
}
|
|
// The sequence
|
|
if ($xmldb_field->getSequence()) {
|
|
if($xmldb_field->getLength()<=9 && $this->sequence_name_small) {
|
|
$sequencename=$this->sequence_name_small;
|
|
} else {
|
|
$sequencename=$this->sequence_name;
|
|
}
|
|
$field .= ' ' . $sequencename;
|
|
if ($this->sequence_only) {
|
|
// We only want the field name and sequence name to be printed
|
|
// so, calculate it and return
|
|
$sql = $this->getEncQuoted($xmldb_field->getName()) . ' ' . $sequencename;
|
|
return $sql;
|
|
}
|
|
}
|
|
return $field;
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_key, returns its specs.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
* @param xmldb_key $xmldb_key The xmldb_key's specifications requested.
|
|
* @return string SQL statement about the xmldb_key.
|
|
*/
|
|
public function getKeySQL($xmldb_table, $xmldb_key) {
|
|
|
|
$key = '';
|
|
|
|
switch ($xmldb_key->getType()) {
|
|
case XMLDB_KEY_PRIMARY:
|
|
if ($this->primary_keys) {
|
|
if ($this->primary_key_name !== null) {
|
|
$key = $this->getEncQuoted($this->primary_key_name);
|
|
} else {
|
|
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'pk');
|
|
}
|
|
$key .= ' PRIMARY KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
}
|
|
break;
|
|
case XMLDB_KEY_UNIQUE:
|
|
if ($this->unique_keys) {
|
|
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'uk');
|
|
$key .= ' UNIQUE (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
}
|
|
break;
|
|
case XMLDB_KEY_FOREIGN:
|
|
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
if ($this->foreign_keys) {
|
|
$key = $this->getNameForObject($xmldb_table->getName(), implode(', ', $xmldb_key->getFields()), 'fk');
|
|
$key .= ' FOREIGN KEY (' . implode(', ', $this->getEncQuoted($xmldb_key->getFields())) . ')';
|
|
$key .= ' REFERENCES ' . $this->getEncQuoted($this->prefix . $xmldb_key->getRefTable());
|
|
$key .= ' (' . implode(', ', $this->getEncQuoted($xmldb_key->getRefFields())) . ')';
|
|
}
|
|
break;
|
|
}
|
|
|
|
return $key;
|
|
}
|
|
|
|
/**
|
|
* Give one xmldb_field, returns the correct "default value" for the current configuration
|
|
*
|
|
* @param xmldb_field $xmldb_field The field.
|
|
* @return The default value of the field.
|
|
*/
|
|
public function getDefaultValue($xmldb_field) {
|
|
|
|
$default = null;
|
|
|
|
if ($xmldb_field->getDefault() !== NULL) {
|
|
if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
|
|
$xmldb_field->getType() == XMLDB_TYPE_TEXT) {
|
|
if ($xmldb_field->getDefault() === '') { // If passing empty default, use the $default_for_char one instead
|
|
$default = "'" . $this->default_for_char . "'";
|
|
} else {
|
|
$default = "'" . $this->addslashes($xmldb_field->getDefault()) . "'";
|
|
}
|
|
} else {
|
|
$default = $xmldb_field->getDefault();
|
|
}
|
|
} else {
|
|
// We force default '' for not null char columns without proper default
|
|
// some day this should be out!
|
|
if ($this->default_for_char !== NULL &&
|
|
$xmldb_field->getType() == XMLDB_TYPE_CHAR &&
|
|
$xmldb_field->getNotNull()) {
|
|
$default = "'" . $this->default_for_char . "'";
|
|
} else {
|
|
// If the DB requires to explicity define some clause to drop one default, do it here
|
|
// never applying defaults to TEXT and BINARY fields
|
|
if ($this->drop_default_value_required &&
|
|
$xmldb_field->getType() != XMLDB_TYPE_TEXT &&
|
|
$xmldb_field->getType() != XMLDB_TYPE_BINARY && !$xmldb_field->getNotNull()) {
|
|
$default = $this->drop_default_value;
|
|
}
|
|
}
|
|
}
|
|
return $default;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_field, returns the correct "default clause" for the current configuration.
|
|
*
|
|
* @param xmldb_field $xmldb_field The xmldb_field.
|
|
* @return The SQL clause for generating the default value as in $xmldb_field.
|
|
*/
|
|
public function getDefaultClause($xmldb_field) {
|
|
|
|
$defaultvalue = $this->getDefaultValue ($xmldb_field);
|
|
|
|
if ($defaultvalue !== null) {
|
|
return ' DEFAULT ' . $defaultvalue;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_table and the new name, returns the SQL statements
|
|
* to rename it (inside one array).
|
|
*
|
|
* @param xmldb_table $xmldb_table The table to rename.
|
|
* @param string $newname The new name to rename the table to.
|
|
* @return array SQL statement(s) to rename the table.
|
|
*/
|
|
public function getRenameTableSQL($xmldb_table, $newname) {
|
|
|
|
$results = array(); //Array where all the sentences will be stored
|
|
|
|
$newt = new xmldb_table($newname); //Temporal table for name calculations
|
|
|
|
$rename = str_replace('OLDNAME', $this->getTableName($xmldb_table), $this->rename_table_sql);
|
|
$rename = str_replace('NEWNAME', $this->getTableName($newt), $rename);
|
|
|
|
$results[] = $rename;
|
|
|
|
// Call to getRenameTableExtraSQL() override if needed
|
|
$extra_sentences = $this->getRenameTableExtraSQL($xmldb_table, $newname);
|
|
$results = array_merge($results, $extra_sentences);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_table, returns the SQL statements
|
|
* to drop it (inside one array). Works also for temporary tables.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table to drop.
|
|
* @return array SQL statement(s) for dropping the specified table.
|
|
*/
|
|
public function getDropTableSQL($xmldb_table) {
|
|
|
|
$results = array(); //Array where all the sentences will be stored
|
|
|
|
$drop = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_table_sql);
|
|
|
|
$results[] = $drop;
|
|
|
|
// call to getDropTableExtraSQL(), override if needed
|
|
$extra_sentences = $this->getDropTableExtraSQL($xmldb_table);
|
|
$results = array_merge($results, $extra_sentences);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Performs any clean up that needs to be done after a table is dropped.
|
|
*
|
|
* @param xmldb_table $table
|
|
*/
|
|
public function cleanup_after_drop(xmldb_table $table): void {
|
|
if ($this->temptables->is_temptable($table->getName())) {
|
|
$this->temptables->delete_temptable($table->getName());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to add the field to the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
* @return array The SQL statement for adding a field to the table.
|
|
*/
|
|
public function getAddFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
|
|
|
|
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
|
|
$results = array();
|
|
|
|
// Get the quoted name of the table and field
|
|
$tablename = $this->getTableName($xmldb_table);
|
|
|
|
// Build the standard alter table add
|
|
$sql = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
|
|
$skip_default_clause,
|
|
$skip_notnull_clause);
|
|
$altertable = 'ALTER TABLE ' . $tablename . ' ADD ' . $sql;
|
|
// Add the after clause if necessary
|
|
if ($this->add_after_clause && $xmldb_field->getPrevious()) {
|
|
$altertable .= ' AFTER ' . $this->getEncQuoted($xmldb_field->getPrevious());
|
|
}
|
|
$results[] = $altertable;
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop the field from the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
* @return array The SQL statement for dropping a field from the table.
|
|
*/
|
|
public function getDropFieldSQL($xmldb_table, $xmldb_field) {
|
|
|
|
$results = array();
|
|
|
|
// Get the quoted name of the table and field
|
|
$tablename = $this->getTableName($xmldb_table);
|
|
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
|
// Build the standard alter table drop
|
|
$results[] = 'ALTER TABLE ' . $tablename . ' DROP COLUMN ' . $fieldname;
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to alter the field in the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to create the SQL from.
|
|
* @param string $skip_type_clause The type clause on alter columns, NULL by default.
|
|
* @param string $skip_default_clause The default clause on alter columns, NULL by default.
|
|
* @param string $skip_notnull_clause The null/notnull clause on alter columns, NULL by default.
|
|
* @return array The field altering SQL statement.
|
|
*/
|
|
public function getAlterFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause = NULL, $skip_default_clause = NULL, $skip_notnull_clause = NULL) {
|
|
|
|
$skip_type_clause = is_null($skip_type_clause) ? $this->alter_column_skip_type : $skip_type_clause;
|
|
$skip_default_clause = is_null($skip_default_clause) ? $this->alter_column_skip_default : $skip_default_clause;
|
|
$skip_notnull_clause = is_null($skip_notnull_clause) ? $this->alter_column_skip_notnull : $skip_notnull_clause;
|
|
|
|
$results = array();
|
|
|
|
// Get the quoted name of the table and field
|
|
$tablename = $this->getTableName($xmldb_table);
|
|
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
|
// Build de alter sentence using the alter_column_sql template
|
|
$alter = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->alter_column_sql);
|
|
$colspec = $this->getFieldSQL($xmldb_table, $xmldb_field, $skip_type_clause,
|
|
$skip_default_clause,
|
|
$skip_notnull_clause,
|
|
true);
|
|
$alter = str_replace('COLUMNSPECS', $colspec, $alter);
|
|
|
|
// Add the after clause if necessary
|
|
if ($this->add_after_clause && $xmldb_field->getPrevious()) {
|
|
$alter .= ' after ' . $this->getEncQuoted($xmldb_field->getPrevious());
|
|
}
|
|
|
|
// Build the standard alter table modify
|
|
$results[] = $alter;
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to modify the default of the field in the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to get the modified default value from.
|
|
* @return array The SQL statement for modifying the default value.
|
|
*/
|
|
public function getModifyDefaultSQL($xmldb_table, $xmldb_field) {
|
|
|
|
$results = array();
|
|
|
|
// Get the quoted name of the table and field
|
|
$tablename = $this->getTableName($xmldb_table);
|
|
$fieldname = $this->getEncQuoted($xmldb_field->getName());
|
|
|
|
// Decide if we are going to create/modify or to drop the default
|
|
if ($xmldb_field->getDefault() === null) {
|
|
$results = $this->getDropDefaultSQL($xmldb_table, $xmldb_field); //Drop
|
|
} else {
|
|
$results = $this->getCreateDefaultSQL($xmldb_table, $xmldb_field); //Create/modify
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one correct xmldb_field and the new name, returns the SQL statements
|
|
* to rename it (inside one array).
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_field.
|
|
* @param xmldb_field $xmldb_field The instance of xmldb_field to get the renamed field from.
|
|
* @param string $newname The new name to rename the field to.
|
|
* @return array The SQL statements for renaming the field.
|
|
*/
|
|
public function getRenameFieldSQL($xmldb_table, $xmldb_field, $newname) {
|
|
|
|
$results = array(); //Array where all the sentences will be stored
|
|
|
|
// Although this is checked in database_manager::rename_field() - double check
|
|
// that we aren't trying to rename one "id" field. Although it could be
|
|
// implemented (if adding the necessary code to rename sequences, defaults,
|
|
// triggers... and so on under each getRenameFieldExtraSQL() function, it's
|
|
// better to forbid it, mainly because this field is the default PK and
|
|
// in the future, a lot of FKs can be pointing here. So, this field, more
|
|
// or less, must be considered immutable!
|
|
if ($xmldb_field->getName() == 'id') {
|
|
return array();
|
|
}
|
|
|
|
$rename = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_column_sql);
|
|
$rename = str_replace('OLDFIELDNAME', $this->getEncQuoted($xmldb_field->getName()), $rename);
|
|
$rename = str_replace('NEWFIELDNAME', $this->getEncQuoted($newname), $rename);
|
|
|
|
$results[] = $rename;
|
|
|
|
// Call to getRenameFieldExtraSQL(), override if needed
|
|
$extra_sentences = $this->getRenameFieldExtraSQL($xmldb_table, $xmldb_field, $newname);
|
|
$results = array_merge($results, $extra_sentences);
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_key, return the SQL statements needed to add the key to the table
|
|
* note that undelying indexes will be added as parametrised by $xxxx_keys and $xxxx_index parameters.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
* @param xmldb_key $xmldb_key The xmldb_key to add.
|
|
* @return array SQL statement to add the xmldb_key.
|
|
*/
|
|
public function getAddKeySQL($xmldb_table, $xmldb_key) {
|
|
|
|
$results = array();
|
|
|
|
// Just use the CreateKeySQL function
|
|
if ($keyclause = $this->getKeySQL($xmldb_table, $xmldb_key)) {
|
|
$key = 'ALTER TABLE ' . $this->getTableName($xmldb_table) .
|
|
' ADD CONSTRAINT ' . $keyclause;
|
|
$results[] = $key;
|
|
}
|
|
|
|
// If we aren't creating the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
// automatically by the RDBMS) create the underlying (created by us) index (if doesn't exists)
|
|
if (!$keyclause || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
// Only if they don't exist
|
|
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN) { //Calculate type of index based on type ok key
|
|
$indextype = XMLDB_INDEX_NOTUNIQUE;
|
|
} else {
|
|
$indextype = XMLDB_INDEX_UNIQUE;
|
|
}
|
|
$xmldb_index = new xmldb_index('anyname', $indextype, $xmldb_key->getFields());
|
|
if (!$this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
|
|
$results = array_merge($results, $this->getAddIndexSQL($xmldb_table, $xmldb_index));
|
|
}
|
|
}
|
|
|
|
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, create it as UNIQUE too
|
|
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
|
|
//Duplicate the key
|
|
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
$results = array_merge($results, $this->getAddKeySQL($xmldb_table, $xmldb_key));
|
|
}
|
|
|
|
// Return results
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
* @param xmldb_key $xmldb_key The xmldb_key to drop.
|
|
* @return array SQL statement to drop the xmldb_key.
|
|
*/
|
|
public function getDropKeySQL($xmldb_table, $xmldb_key) {
|
|
|
|
$results = array();
|
|
|
|
// Get the key name (note that this doesn't introspect DB, so could cause some problems sometimes!)
|
|
// TODO: We'll need to overwrite the whole getDropKeySQL() method inside each DB to do the proper queries
|
|
// against the dictionary or require ADOdb to support it or change the find_key_name() method to
|
|
// perform DB introspection directly. But, for now, as we aren't going to enable referential integrity
|
|
// it won't be a problem at all
|
|
$dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
|
|
|
|
// Only if such type of key generation is enabled
|
|
$dropkey = false;
|
|
switch ($xmldb_key->getType()) {
|
|
case XMLDB_KEY_PRIMARY:
|
|
if ($this->primary_keys) {
|
|
$template = $this->drop_primary_key;
|
|
$dropkey = true;
|
|
}
|
|
break;
|
|
case XMLDB_KEY_UNIQUE:
|
|
if ($this->unique_keys) {
|
|
$template = $this->drop_unique_key;
|
|
$dropkey = true;
|
|
}
|
|
break;
|
|
case XMLDB_KEY_FOREIGN_UNIQUE:
|
|
case XMLDB_KEY_FOREIGN:
|
|
if ($this->foreign_keys) {
|
|
$template = $this->drop_foreign_key;
|
|
$dropkey = true;
|
|
}
|
|
break;
|
|
}
|
|
// If we have decided to drop the key, let's do it
|
|
if ($dropkey) {
|
|
// Replace TABLENAME, CONSTRAINTTYPE and KEYNAME as needed
|
|
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $template);
|
|
$dropsql = str_replace('KEYNAME', $dbkeyname, $dropsql);
|
|
|
|
$results[] = $dropsql;
|
|
}
|
|
|
|
// If we aren't dropping the keys OR if the key is XMLDB_KEY_FOREIGN (not underlying index generated
|
|
// automatically by the RDBMS) drop the underlying (created by us) index (if exists)
|
|
if (!$dropkey || $xmldb_key->getType() == XMLDB_KEY_FOREIGN) {
|
|
// Only if they exist
|
|
$xmldb_index = new xmldb_index('anyname', XMLDB_INDEX_UNIQUE, $xmldb_key->getFields());
|
|
if ($this->mdb->get_manager()->index_exists($xmldb_table, $xmldb_index)) {
|
|
$results = array_merge($results, $this->getDropIndexSQL($xmldb_table, $xmldb_index));
|
|
}
|
|
}
|
|
|
|
// If the key is XMLDB_KEY_FOREIGN_UNIQUE, drop the UNIQUE too
|
|
if ($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && $this->unique_keys) {
|
|
//Duplicate the key
|
|
$xmldb_key->setType(XMLDB_KEY_UNIQUE);
|
|
$results = array_merge($results, $this->getDropKeySQL($xmldb_table, $xmldb_key));
|
|
}
|
|
|
|
// Return results
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_key, return the SQL statements needed to rename the key in the table
|
|
* Experimental! Shouldn't be used at all!
|
|
*
|
|
* @param xmldb_table $xmldb_table The table related to $xmldb_key.
|
|
* @param xmldb_key $xmldb_key The xmldb_key to rename.
|
|
* @param string $newname The xmldb_key's new name.
|
|
* @return array SQL statement to rename the xmldb_key.
|
|
*/
|
|
public function getRenameKeySQL($xmldb_table, $xmldb_key, $newname) {
|
|
|
|
$results = array();
|
|
|
|
// Get the real key name
|
|
$dbkeyname = $this->mdb->get_manager()->find_key_name($xmldb_table, $xmldb_key);
|
|
|
|
// Check we are really generating this type of keys
|
|
if (($xmldb_key->getType() == XMLDB_KEY_PRIMARY && !$this->primary_keys) ||
|
|
($xmldb_key->getType() == XMLDB_KEY_UNIQUE && !$this->unique_keys) ||
|
|
($xmldb_key->getType() == XMLDB_KEY_FOREIGN && !$this->foreign_keys) ||
|
|
($xmldb_key->getType() == XMLDB_KEY_FOREIGN_UNIQUE && !$this->unique_keys && !$this->foreign_keys)) {
|
|
// We aren't generating this type of keys, delegate to child indexes
|
|
$xmldb_index = new xmldb_index($xmldb_key->getName());
|
|
$xmldb_index->setFields($xmldb_key->getFields());
|
|
return $this->getRenameIndexSQL($xmldb_table, $xmldb_index, $newname);
|
|
}
|
|
|
|
// Arrived here so we are working with keys, lets rename them
|
|
// Replace TABLENAME and KEYNAME as needed
|
|
$renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_key_sql);
|
|
$renamesql = str_replace('OLDKEYNAME', $dbkeyname, $renamesql);
|
|
$renamesql = str_replace('NEWKEYNAME', $newname, $renamesql);
|
|
|
|
// Some DB doesn't support key renaming so this can be empty
|
|
if ($renamesql) {
|
|
$results[] = $renamesql;
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to add the index to the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table instance to add the index on.
|
|
* @param xmldb_index $xmldb_index The xmldb_index to add.
|
|
* @return array An array of SQL statements to add the index.
|
|
*/
|
|
public function getAddIndexSQL($xmldb_table, $xmldb_index) {
|
|
|
|
// Just use the CreateIndexSQL function
|
|
return $this->getCreateIndexSQL($xmldb_table, $xmldb_index);
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to drop the index from the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table instance to drop the index on.
|
|
* @param xmldb_index $xmldb_index The xmldb_index to drop.
|
|
* @return array An array of SQL statements to drop the index.
|
|
*/
|
|
public function getDropIndexSQL($xmldb_table, $xmldb_index) {
|
|
|
|
$results = array();
|
|
|
|
// Get the real index name
|
|
$dbindexnames = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index, true);
|
|
|
|
// Replace TABLENAME and INDEXNAME as needed
|
|
if ($dbindexnames) {
|
|
foreach ($dbindexnames as $dbindexname) {
|
|
$dropsql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->drop_index_sql);
|
|
$dropsql = str_replace('INDEXNAME', $this->getEncQuoted($dbindexname), $dropsql);
|
|
$results[] = $dropsql;
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_index, return the SQL statements needed to rename the index in the table
|
|
* Experimental! Shouldn't be used at all!
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table instance to rename the index on.
|
|
* @param xmldb_index $xmldb_index The xmldb_index to rename.
|
|
* @param string $newname The xmldb_index's new name.
|
|
* @return array An array of SQL statements to rename the index.
|
|
*/
|
|
function getRenameIndexSQL($xmldb_table, $xmldb_index, $newname) {
|
|
// Some DB doesn't support index renaming (MySQL) so this can be empty
|
|
if (empty($this->rename_index_sql)) {
|
|
return array();
|
|
}
|
|
|
|
// Get the real index name
|
|
$dbindexname = $this->mdb->get_manager()->find_index_name($xmldb_table, $xmldb_index);
|
|
// Replace TABLENAME and INDEXNAME as needed
|
|
$renamesql = str_replace('TABLENAME', $this->getTableName($xmldb_table), $this->rename_index_sql);
|
|
$renamesql = str_replace('OLDINDEXNAME', $this->getEncQuoted($dbindexname), $renamesql);
|
|
$renamesql = str_replace('NEWINDEXNAME', $this->getEncQuoted($newname), $renamesql);
|
|
|
|
return array($renamesql);
|
|
}
|
|
|
|
/**
|
|
* Given three strings (table name, list of fields (comma separated) and suffix),
|
|
* create the proper object name quoting it if necessary.
|
|
*
|
|
* IMPORTANT: This function must be used to CALCULATE NAMES of objects TO BE CREATED,
|
|
* NEVER TO GUESS NAMES of EXISTING objects!!!
|
|
*
|
|
* @param string $tablename The table name.
|
|
* @param string $fields A list of comma separated fields.
|
|
* @param string $suffix A suffix for the object name.
|
|
* @return string Object's name.
|
|
*/
|
|
public function getNameForObject($tablename, $fields, $suffix='') {
|
|
|
|
$name = '';
|
|
|
|
// Implement one basic cache to avoid object name duplication
|
|
// along all the request life, but never to return cached results
|
|
// We need this because sql statements are created before executing
|
|
// them, hence names doesn't exist "physically" yet in DB, so we need
|
|
// to known which ones have been used.
|
|
// We track all the keys used, and the previous counters to make subsequent creates faster.
|
|
// This may happen a lot with things like bulk backups or restores.
|
|
static $usednames = array();
|
|
static $previouscounters = array();
|
|
|
|
// Use standard naming. See http://docs.moodle.org/en/XMLDB_key_and_index_naming
|
|
$tablearr = explode ('_', $tablename);
|
|
foreach ($tablearr as $table) {
|
|
$name .= substr(trim($table),0,4);
|
|
}
|
|
$name .= '_';
|
|
$fieldsarr = explode (',', $fields);
|
|
foreach ($fieldsarr as $field) {
|
|
$name .= substr(trim($field),0,3);
|
|
}
|
|
// Prepend the prefix
|
|
$name = trim($this->prefix . $name);
|
|
|
|
// Make sure name does not exceed the maximum name length and add suffix.
|
|
$maxlengthwithoutsuffix = $this->names_max_length - strlen($suffix) - ($suffix ? 1 : 0);
|
|
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix) . ($suffix ? ('_' . $suffix) : '');
|
|
|
|
if (isset($previouscounters[$name])) {
|
|
// If we have a counter stored, we will need to modify the key to the next counter location.
|
|
$counter = $previouscounters[$name] + 1;
|
|
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
|
|
$counter . ($suffix ? ('_' . $suffix) : '');
|
|
} else {
|
|
$counter = 1;
|
|
}
|
|
|
|
// If the calculated name is in the cache, or if we detect it by introspecting the DB let's modify it.
|
|
while (isset($usednames[$namewithsuffix]) || $this->isNameInUse($namewithsuffix, $suffix, $tablename)) {
|
|
// Now iterate until not used name is found, incrementing the counter
|
|
$counter++;
|
|
$namewithsuffix = substr($name, 0, $maxlengthwithoutsuffix - strlen($counter)) .
|
|
$counter . ($suffix ? ('_' . $suffix) : '');
|
|
}
|
|
|
|
// Add the name to the cache. Using key look with isset because it is much faster than in_array.
|
|
$usednames[$namewithsuffix] = true;
|
|
$previouscounters[$name] = $counter;
|
|
|
|
// Quote it if necessary (reserved words)
|
|
$namewithsuffix = $this->getEncQuoted($namewithsuffix);
|
|
|
|
return $namewithsuffix;
|
|
}
|
|
|
|
/**
|
|
* Given any string (or one array), enclose it by the proper quotes
|
|
* if it's a reserved word
|
|
*
|
|
* @param string|array $input String to quote.
|
|
* @return string|array Quoted string.
|
|
*/
|
|
public function getEncQuoted($input) {
|
|
|
|
if (is_array($input)) {
|
|
foreach ($input as $key=>$content) {
|
|
$input[$key] = $this->getEncQuoted($content);
|
|
}
|
|
return $input;
|
|
} else {
|
|
// Always lowercase
|
|
$input = strtolower($input);
|
|
// if reserved or quote_all or has hyphens, quote it
|
|
if ($this->quote_all || in_array($input, $this->reserved_words) || strpos($input, '-') !== false) {
|
|
$input = $this->quote_string . $input . $this->quote_string;
|
|
}
|
|
return $input;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given one XMLDB Statement, build the needed SQL insert sentences to execute it.
|
|
*
|
|
* @param mixed $statement SQL statement.
|
|
* @return array Array of sentences in the SQL statement.
|
|
*/
|
|
function getExecuteInsertSQL($statement) {
|
|
|
|
$results = array(); //Array where all the sentences will be stored
|
|
|
|
if ($sentences = $statement->getSentences()) {
|
|
foreach ($sentences as $sentence) {
|
|
// Get the list of fields
|
|
$fields = $statement->getFieldsFromInsertSentence($sentence);
|
|
// Get the values of fields
|
|
$values = $statement->getValuesFromInsertSentence($sentence);
|
|
// Look if we have some CONCAT value and transform it dynamically
|
|
foreach($values as $key => $value) {
|
|
// Trim single quotes
|
|
$value = trim($value,"'");
|
|
if (stristr($value, 'CONCAT') !== false){
|
|
// Look for data between parenthesis
|
|
preg_match("/CONCAT\s*\((.*)\)$/is", trim($value), $matches);
|
|
if (isset($matches[1])) {
|
|
$part = $matches[1];
|
|
// Convert the comma separated string to an array
|
|
$arr = xmldb_object::comma2array($part);
|
|
if ($arr) {
|
|
$value = $this->getConcatSQL($arr);
|
|
}
|
|
}
|
|
}
|
|
// Values to be sent to DB must be properly escaped
|
|
$value = $this->addslashes($value);
|
|
// Back trimmed quotes
|
|
$value = "'" . $value . "'";
|
|
// Back to the array
|
|
$values[$key] = $value;
|
|
}
|
|
|
|
// Iterate over fields, escaping them if necessary
|
|
foreach($fields as $key => $field) {
|
|
$fields[$key] = $this->getEncQuoted($field);
|
|
}
|
|
// Build the final SQL sentence and add it to the array of results
|
|
$sql = 'INSERT INTO ' . $this->getEncQuoted($this->prefix . $statement->getTable()) .
|
|
'(' . implode(', ', $fields) . ') ' .
|
|
'VALUES (' . implode(', ', $values) . ')';
|
|
$results[] = $sql;
|
|
}
|
|
|
|
}
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Given one array of elements, build the proper CONCAT expression, based
|
|
* in the $concat_character setting. If such setting is empty, then
|
|
* MySQL's CONCAT function will be used instead.
|
|
*
|
|
* @param array $elements An array of elements to concatenate.
|
|
* @return mixed Returns the result of moodle_database::sql_concat() or false.
|
|
* @uses moodle_database::sql_concat()
|
|
* @uses call_user_func_array()
|
|
*/
|
|
public function getConcatSQL($elements) {
|
|
|
|
// Replace double quoted elements by single quotes
|
|
foreach($elements as $key => $element) {
|
|
$element = trim($element);
|
|
if (substr($element, 0, 1) == '"' &&
|
|
substr($element, -1, 1) == '"') {
|
|
$elements[$key] = "'" . trim($element, '"') . "'";
|
|
}
|
|
}
|
|
|
|
// Now call the standard $DB->sql_concat() DML function
|
|
return call_user_func_array(array($this->mdb, 'sql_concat'), array_values($elements));
|
|
}
|
|
|
|
/**
|
|
* Returns the name (string) of the sequence used in the table for the autonumeric pk
|
|
* Only some DB have this implemented.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table instance.
|
|
* @return bool Returns the sequence from the DB or false.
|
|
*/
|
|
public function getSequenceFromDB($xmldb_table) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Given one object name and it's type (pk, uk, fk, ck, ix, uix, seq, trg).
|
|
*
|
|
* (MySQL requires the whole xmldb_table object to be specified, so we add it always)
|
|
*
|
|
* This is invoked from getNameForObject().
|
|
* Only some DB have this implemented.
|
|
*
|
|
* @param string $object_name The object's name to check for.
|
|
* @param string $type The object's type (pk, uk, fk, ck, ix, uix, seq, trg).
|
|
* @param string $table_name The table's name to check in
|
|
* @return bool If such name is currently in use (true) or no (false)
|
|
*/
|
|
public function isNameInUse($object_name, $type, $table_name) {
|
|
return false; //For generators not implementing introspection,
|
|
//we always return with the name being free to be used
|
|
}
|
|
|
|
|
|
// ====== FOLLOWING FUNCTION MUST BE CUSTOMISED BY ALL THE XMLDGenerator classes ========
|
|
|
|
/**
|
|
* Reset a sequence to the id field of a table.
|
|
*
|
|
* @param xmldb_table|string $table name of table or the table object.
|
|
* @return array of sql statements
|
|
*/
|
|
abstract public function getResetSequenceSQL($table);
|
|
|
|
/**
|
|
* Given one correct xmldb_table, returns the SQL statements
|
|
* to create temporary table (inside one array).
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @return array of sql statements
|
|
*/
|
|
abstract public function getCreateTempTableSQL($xmldb_table);
|
|
|
|
/**
|
|
* Given one XMLDB Type, length and decimals, returns the DB proper SQL type.
|
|
*
|
|
* @param int $xmldb_type The xmldb_type defined constant. XMLDB_TYPE_INTEGER and other XMLDB_TYPE_* constants.
|
|
* @param int $xmldb_length The length of that data type.
|
|
* @param int $xmldb_decimals The decimal places of precision of the data type.
|
|
* @return string The DB defined data type.
|
|
*/
|
|
abstract public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null);
|
|
|
|
/**
|
|
* Returns the code (array of statements) needed to execute extra statements on field rename.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
* @return array Array of extra SQL statements to run with a field being renamed.
|
|
*/
|
|
public function getRenameFieldExtraSQL($xmldb_table, $xmldb_field) {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Returns the code (array of statements) needed
|
|
* to create one sequence for the xmldb_table and xmldb_field passed in.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
* @return array Array of SQL statements to create the sequence.
|
|
*/
|
|
public function getCreateSequenceSQL($xmldb_table, $xmldb_field) {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Returns the code (array of statements) needed to add one comment to the table.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @return array Array of SQL statements to add one comment to the table.
|
|
*/
|
|
abstract public function getCommentSQL($xmldb_table);
|
|
|
|
/**
|
|
* Returns the code (array of statements) needed to execute extra statements on table rename.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @param string $newname The new name for the table.
|
|
* @return array Array of extra SQL statements to rename a table.
|
|
*/
|
|
public function getRenameTableExtraSQL($xmldb_table, $newname) {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Returns the code (array of statements) needed to execute extra statements on table drop
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @return array Array of extra SQL statements to drop a table.
|
|
*/
|
|
public function getDropTableExtraSQL($xmldb_table) {
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to drop its default
|
|
* (usually invoked from getModifyDefaultSQL()
|
|
*
|
|
* Note that this method may be dropped in future.
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
* @return array Array of SQL statements to create a field's default.
|
|
*
|
|
* @todo MDL-31147 Moodle 2.1 - Drop getDropDefaultSQL()
|
|
*/
|
|
abstract public function getDropDefaultSQL($xmldb_table, $xmldb_field);
|
|
|
|
/**
|
|
* Given one xmldb_table and one xmldb_field, return the SQL statements needed to add its default
|
|
* (usually invoked from getModifyDefaultSQL()
|
|
*
|
|
* @param xmldb_table $xmldb_table The xmldb_table object instance.
|
|
* @param xmldb_field $xmldb_field The xmldb_field object instance.
|
|
* @return array Array of SQL statements to create a field's default.
|
|
*/
|
|
abstract public function getCreateDefaultSQL($xmldb_table, $xmldb_field);
|
|
|
|
/**
|
|
* Returns an array of reserved words (lowercase) for this DB
|
|
* You MUST provide the real list for each DB inside every XMLDB class.
|
|
* @return array An array of database specific reserved words.
|
|
* @throws coding_exception Thrown if not implemented for the specific DB.
|
|
*/
|
|
public static function getReservedWords() {
|
|
throw new coding_exception('getReservedWords() method needs to be overridden in each subclass of sql_generator');
|
|
}
|
|
|
|
/**
|
|
* Returns all reserved words in supported databases.
|
|
* Reserved words should be lowercase.
|
|
* @return array ('word'=>array(databases))
|
|
*/
|
|
public static function getAllReservedWords() {
|
|
global $CFG;
|
|
|
|
$generators = array('mysql', 'postgres', 'oracle', 'mssql');
|
|
$reserved_words = array();
|
|
|
|
foreach($generators as $generator) {
|
|
$class = $generator . '_sql_generator';
|
|
require_once("$CFG->libdir/ddl/$class.php");
|
|
foreach (call_user_func(array($class, 'getReservedWords')) as $word) {
|
|
$reserved_words[$word][] = $generator;
|
|
}
|
|
}
|
|
ksort($reserved_words);
|
|
return $reserved_words;
|
|
}
|
|
|
|
/**
|
|
* Adds slashes to string.
|
|
* @param string $s
|
|
* @return string The escaped string.
|
|
*/
|
|
public function addslashes($s) {
|
|
// do not use php addslashes() because it depends on PHP quote settings!
|
|
$s = str_replace('\\','\\\\',$s);
|
|
$s = str_replace("\0","\\\0", $s);
|
|
$s = str_replace("'", "\\'", $s);
|
|
return $s;
|
|
}
|
|
|
|
/**
|
|
* Get the fields from an index definition that might be null.
|
|
* @param xmldb_table $xmldb_table the table
|
|
* @param xmldb_index $xmldb_index the index
|
|
* @return array list of fields in the index definition that might be null.
|
|
*/
|
|
public function get_nullable_fields_in_index($xmldb_table, $xmldb_index) {
|
|
global $DB;
|
|
|
|
// If we don't have the field info passed in, we need to query it from the DB.
|
|
$fieldsfromdb = null;
|
|
|
|
$nullablefields = [];
|
|
foreach ($xmldb_index->getFields() as $fieldname) {
|
|
if ($field = $xmldb_table->getField($fieldname)) {
|
|
// We have the field details in the table definition.
|
|
if ($field->getNotNull() !== XMLDB_NOTNULL) {
|
|
$nullablefields[] = $fieldname;
|
|
}
|
|
|
|
} else {
|
|
// We don't have the table definition loaded. Need to
|
|
// inspect the database.
|
|
if ($fieldsfromdb === null) {
|
|
$fieldsfromdb = $DB->get_columns($xmldb_table->getName(), false);
|
|
}
|
|
if (!isset($fieldsfromdb[$fieldname])) {
|
|
throw new coding_exception('Unknown field ' . $fieldname .
|
|
' in index ' . $xmldb_index->getName());
|
|
}
|
|
|
|
if (!$fieldsfromdb[$fieldname]->not_null) {
|
|
$nullablefields[] = $fieldname;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $nullablefields;
|
|
}
|
|
}
|