mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 04:30:15 +01:00
Initial commit of the base generator class.
This commit is contained in:
parent
b169c31e39
commit
91496d15bc
473
lib/xmldb/classes/generators/XMLDBGenerator.class.php
Normal file
473
lib/xmldb/classes/generators/XMLDBGenerator.class.php
Normal file
@ -0,0 +1,473 @@
|
||||
<?php // $Id$
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// NOTICE OF COPYRIGHT //
|
||||
// //
|
||||
// Moodle - Modular Object-Oriented Dynamic Learning Environment //
|
||||
// http://moodle.com //
|
||||
// //
|
||||
// Copyright (C) 2001-3001 Martin Dougiamas http://dougiamas.com //
|
||||
// (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com //
|
||||
// //
|
||||
// This program 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 2 of the License, or //
|
||||
// (at your option) any later version. //
|
||||
// //
|
||||
// This program 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: //
|
||||
// //
|
||||
// http://www.gnu.org/copyleft/gpl.html //
|
||||
// //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// This class represent the base generator class where all the
|
||||
/// needed functions to generate proper SQL are defined.
|
||||
|
||||
/// If fact, this class generate SQL code to be used against MySQL
|
||||
/// so the rest of classes will inherit, by default, the same logic.
|
||||
/// Functions will be overriden as needed to generate correct SQL.
|
||||
|
||||
class XMLDBgenerator {
|
||||
|
||||
/// 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 $quote_string = '"'; // String used to quote names
|
||||
var $quote_all = false; // To decide if we want to quote all the names or only the reserved ones
|
||||
|
||||
var $integer_to_number = false; // To create all the integers as NUMBER(x) (also called DECIMAL, NUMERIC...)
|
||||
var $float_to_number = false; // To create all the floats as NUMBER(x) (also called DECIMAL, NUMERIC...)
|
||||
|
||||
var $number_type = 'NUMERIC'; // Proper type for NUMBER(x) in this DB
|
||||
|
||||
var $unsigned_allowed = true; // To define in the generator must handle unsigned information
|
||||
var $default_for_char = ''; // To define the default to set for NOT NULLs CHARs without default (null=do nothing)
|
||||
|
||||
var $primary_key_name = null; //To force primary key names to one string (null=no force)
|
||||
|
||||
var $primary_keys = true; // Does the generator build primary keys
|
||||
var $unique_keys = true; // Does the generator build unique keys
|
||||
var $foreign_keys = true; // Does the generator build foreign keys
|
||||
|
||||
var $primary_index = true;// Does the generator need to build one index for primary keys
|
||||
var $unique_index = true; // Does the generator need to build one index for unique keys
|
||||
var $foreign_index = true; // Does the generator need to build one index for foreign keys
|
||||
|
||||
var $sequence_extra_code = true; //Does the generator need to add extra code to generate the sequence fields
|
||||
var $sequence_name = 'auto_increment'; //Particular name for inline sequences in this generator
|
||||
var $sequence_only = false; //To avoid to output the rest of the field specs, leaving only the name and the sequence_name variable
|
||||
|
||||
var $enum_extra_code = true; //Does the generator need to add extra code to generate code for the enums in the table
|
||||
|
||||
var $add_table_comments = true; // Does the generator need to add code for table comments
|
||||
|
||||
var $prefix_on_names = true; //Does the generator need to prepend the prefix to all the key/index/sequence/trigger/check names
|
||||
var $names_max_length = 30; //Max length for key/index/sequence/trigger/check names
|
||||
|
||||
var $prefix; // Prefix to be used for all the DB objects
|
||||
|
||||
var $reserved_words; // List of reserved words (in order to quote them properly)
|
||||
|
||||
|
||||
/**
|
||||
* Creates one new XMLDBGenerator
|
||||
*/
|
||||
function XMLDBgenerator() {
|
||||
global $CFG;
|
||||
$this->prefix = '';
|
||||
$this->reserved_words = $this->getReservedWords();
|
||||
}
|
||||
|
||||
/// ALL THESE FUNCTION ARE SHARED BY ALL THE XMLDGenerator classes
|
||||
|
||||
/**
|
||||
* Set the prefix
|
||||
*/
|
||||
function setPrefix($prefix) {
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one correct XMLDBTable, returns the complete SQL lines to create it
|
||||
*/
|
||||
function getCreateTableSQL($xmldb_table) {
|
||||
|
||||
/// Table header
|
||||
$table = 'CREATE TABLE ' . $this->getEncQuoted($this->prefix . $xmldb_table->getName()) . ' (';
|
||||
|
||||
if (!$xmldb_fields = $xmldb_table->getFields()) {
|
||||
return false;
|
||||
}
|
||||
/// Add the fields, separated by commas
|
||||
foreach ($xmldb_fields as $xmldb_field) {
|
||||
$table .= "\n " . $this->getFieldSQL($xmldb_field) . ',';
|
||||
}
|
||||
/// 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 . ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Add enum extra code if needed
|
||||
if ($this->enum_extra_code) {
|
||||
/// Iterate over fields looking for sequences
|
||||
foreach ($xmldb_fields as $xmldb_field) {
|
||||
if ($xmldb_field->getEnum()) {
|
||||
$table .= "\n" . $this->getEnumExtraSQL($xmldb_table, $xmldb_field) . ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Table footer, trim the latest comma
|
||||
$table = trim($table,',');
|
||||
$table .= "\n)";
|
||||
|
||||
/// Add comments if specified
|
||||
if ($this->add_table_comments) {
|
||||
$table .= $this->getCommentSQL ($xmldb_table) . "\n";
|
||||
}
|
||||
/// Add the indexes (each one, one statement)
|
||||
$indexcombs = array(); //To store all the key combinations used
|
||||
if ($xmldb_indexes = $xmldb_table->getIndexes()) {
|
||||
foreach ($xmldb_indexes as $xmldb_index) {
|
||||
$fieldsarr = $xmldb_index->getFields();
|
||||
sort ($fieldsarr);
|
||||
$currentcomb = strtolower(implode('-', $fieldsarr));
|
||||
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $xmldb_index)) {
|
||||
/// Only create the index if the combination hasn't been used before
|
||||
if (!in_array($currentcomb, $indexcombs)) {
|
||||
$table .= "\n" . $indextext;
|
||||
}
|
||||
}
|
||||
/// Add the index to the array of used combinations
|
||||
$indexcombs[] = $currentcomb;
|
||||
}
|
||||
}
|
||||
/// 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) {
|
||||
$index = null;
|
||||
switch ($xmldb_key->getType()) {
|
||||
case XMLDB_KEY_PRIMARY:
|
||||
if ($this->primary_index) {
|
||||
$index = new XMLDBIndex('temp_index');
|
||||
$index->setUnique(true);
|
||||
$index->setFields($xmldb_key->getFields());
|
||||
}
|
||||
break;
|
||||
case XMLDB_KEY_UNIQUE:
|
||||
case XMLDB_KEY_FOREIGN_UNIQUE:
|
||||
if ($this->unique_index) {
|
||||
$index = new XMLDBIndex('temp_index');
|
||||
$index->setUnique(true);
|
||||
$index->setFields($xmldb_key->getFields());
|
||||
}
|
||||
break;
|
||||
case XMLDB_KEY_FOREIGN:
|
||||
if ($this->foreign_index) {
|
||||
$index = new XMLDBIndex('temp_index');
|
||||
$index->setUnique(false);
|
||||
$index->setFields($xmldb_key->getFields());
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($index) {
|
||||
if ($indextext = $this->getCreateIndexSQL($xmldb_table, $index)) {
|
||||
$fieldsarr = $index->getFields();
|
||||
sort ($fieldsarr);
|
||||
$currentcomb = strtolower(implode('-', $fieldsarr));
|
||||
/// Only create the index if the combination hasn't been used before
|
||||
if (!in_array($currentcomb, $indexcombs)) {
|
||||
$table .= "\n" . $indextext;
|
||||
}
|
||||
}
|
||||
/// Add the index to the array of used combinations
|
||||
$indexcombs[] = $currentcomb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()) {
|
||||
$table .= "\n" . $this->getCreateSequenceSQL($xmldb_table, $xmldb_field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one correct XMLDBIndex, returns the complete SQL line to create it
|
||||
*/
|
||||
function getCreateIndexSQL ($xmldb_table, $xmldb_index) {
|
||||
|
||||
$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->prefix . $this->getEncQuoted($xmldb_table->getName());
|
||||
$index .= ' (' . implode(', ', $this->getEncQuoted($xmldb_index->getFields())) . ');';
|
||||
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one correct XMLDBField, returns the complete SQL line to create it
|
||||
*/
|
||||
function getFieldSQL($xmldb_field) {
|
||||
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
/// The name
|
||||
$field = $this->getEncQuoted($xmldb_field->getName());
|
||||
/// The type and length (if the field isn't enum)
|
||||
if (!$xmldb_field->getEnum()) {
|
||||
$field .= ' ' . $this->getTypeSQL($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
|
||||
} else {
|
||||
/// call to custom function
|
||||
$field .= ' ' . $this->getEnumSQL($xmldb_field);
|
||||
}
|
||||
/// The unsigned if supported
|
||||
if ($this->unsigned_allowed && ($xmldb_field->getType() == XMLDB_TYPE_INTEGER ||
|
||||
$xmldb_field->getType() == XMLDB_TYPE_NUMBER ||
|
||||
$xmldb_field->getType() == XMLDB_TYPE_FLOAT)) {
|
||||
if ($xmldb_field->getUnsigned()) {
|
||||
$field .= ' unsigned';
|
||||
}
|
||||
}
|
||||
/// The not null
|
||||
if ($xmldb_field->getNotNull()) {
|
||||
$field .= ' NOT NULL';
|
||||
}
|
||||
/// The sequence
|
||||
if ($xmldb_field->getSequence()) {
|
||||
$field .= ' ' . $this->sequence_name;
|
||||
if ($this->sequence_only) {
|
||||
/// We only want the field name and sequence name to be printed
|
||||
/// so, calculate it and return
|
||||
return $this->getEncQuoted($xmldb_field->getName()) . ' ' . $this->sequence_name;
|
||||
}
|
||||
}
|
||||
/// The default
|
||||
if ($xmldb_field->getDefault() != NULL) {
|
||||
$field .= ' default ';
|
||||
if ($xmldb_field->getType() == XMLDB_TYPE_CHAR ||
|
||||
$xmldb_field->getType() == XMLDB_TYPE_TEXT) {
|
||||
$field .= "'" . $xmldb_field->getDefault() . "'";
|
||||
} else {
|
||||
$field .= $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()) {
|
||||
$field .= ' default ' . "'" . $this->default_for_char . "'";
|
||||
}
|
||||
}
|
||||
return $field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one correct XMLDBKey, returns its specs
|
||||
*/
|
||||
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->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 KEY (' . 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($xmldb_key->getRefTable());
|
||||
$key .= ' (' . implode(', ', $this->getEncQuoted($xmldb_key->getRefFields())) . ')';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given three strings (table name, list of fields (comma separated) and suffix), create the proper object name
|
||||
*/
|
||||
function getNameForObject($tablename, $fields, $suffix) {
|
||||
|
||||
$name = '';
|
||||
|
||||
/// Implement one basic cache to avoid object name duplication
|
||||
if (!isset($used_names)) {
|
||||
static $used_names = 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,3);
|
||||
}
|
||||
$name .= '_';
|
||||
$fieldsarr = explode (',', $fields);
|
||||
foreach ($fieldsarr as $field) {
|
||||
$name .= substr(trim($field),0,3);
|
||||
}
|
||||
/// Prepend the prefix if required
|
||||
if ($this->prefix_on_names) {
|
||||
$name = $this->prefix . $name;
|
||||
}
|
||||
$name = substr(trim($name), 0, $this->names_max_length - 1 - strlen($suffix)); //Max names_max_length
|
||||
|
||||
/// Add the suffix
|
||||
$namewithsuffix = $name . '_' . $suffix;
|
||||
|
||||
/// If the calculated name is in the cache, let's modify if
|
||||
if (in_array($namewithsuffix, $used_names)) {
|
||||
$counter = 2;
|
||||
/// If have free space, we add 2
|
||||
if (strlen($namewithsuffix) < 30) {
|
||||
$newname = $name . $counter;
|
||||
/// Else replace the last char by 2
|
||||
} else {
|
||||
$newname = substr($name, 0, strlen($name)-1) . $counter;
|
||||
}
|
||||
$newnamewithsuffix = $newname . '_' . $suffix;
|
||||
/// Now iterate until not used name is found, incrementing the counter
|
||||
while (in_array($newnamewithsuffix, $used_names)) {
|
||||
$newname = substr($name, 0, strlen($newname)-1) . $counter;
|
||||
$newnamewithsuffix = $newname . '_' . $suffix;
|
||||
$counter++;
|
||||
}
|
||||
$namewithsuffix = $newnamewithsuffix;
|
||||
}
|
||||
|
||||
/// Add the name to the cache
|
||||
$used_names[] = $namewithsuffix;
|
||||
|
||||
return $namewithsuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given any string (or one array), enclose it by the proper quotes
|
||||
*/
|
||||
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, quote it
|
||||
if ($this->quote_all || in_array($input, $this->reserved_words)) {
|
||||
$input = $this->quote_string . $input . $this->quote_string;
|
||||
}
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
/// ALL THESE FUNCTION MUST BE CUSTOMISED BY ALL THE XMLDGenerator classes
|
||||
|
||||
/**
|
||||
* Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
|
||||
*/
|
||||
function getTypeSQL ($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
|
||||
return 'code for type(precision) goes to function getTypeSQL()';
|
||||
}
|
||||
|
||||
/**
|
||||
* Given one XMLDB Field, return its enum SQL
|
||||
*/
|
||||
function getEnumSQL ($xmldb_field) {
|
||||
return 'code for inline enum declaration goes to function getEnumSQL()';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code needed to create one enum for the xmldb_table and xmldb_field passes
|
||||
*/
|
||||
function getEnumExtraSQL ($xmldb_table, $xmldb_field) {
|
||||
return 'Code for extra enum SQL goes to getEnumExtraSQL(). Can be disabled with enum_extra_code=false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code needed to create one sequence for the xmldb_table and xmldb_field passes
|
||||
*/
|
||||
function getCreateSequenceSQL ($xmldb_table, $xmldb_field) {
|
||||
return 'Code for extra sequence SQL goes to getCreateSequenceSQL(). Can be disabled with sequence_extra_code=false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the code needed to add one comment to the table
|
||||
*/
|
||||
function getCommentSQL ($xmldb_table) {
|
||||
return 'Code for table comment goes to getCommentSQL(). Can be disabled with add_table_comments=false;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of reserved words (lowercase) for this DB
|
||||
* You MUST provide the real list for each DB inside every XMLDB class
|
||||
*/
|
||||
function getReservedWords() {
|
||||
/// Some wel-know reserved words
|
||||
$reserved_words = array (
|
||||
'user', 'scale', 'type', 'comment'
|
||||
);
|
||||
return $reserved_words;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Loading…
x
Reference in New Issue
Block a user