--- /dev/null
+<?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 file contains all the constants and variables used
+/// by the XMLDB interface
+
+/// First, some constants to be used by actions
+ define('ACTION_NONE', 0); //Default flags for class
+ define('ACTION_GENERATE_HTML', 1); //The invoke function will return HTML
+ define('ACTION_GENERATE_XML', 2); //The invoke function will return HTML
+ define('ACTION_HAVE_SUBACTIONS', 1); //The class can have subaction
+
+/// Now the allowed DB Field Types
+ define ('XMLDB_TYPE_INCORRECT', 0); //Wrong DB Type
+ define ('XMLDB_TYPE_INTEGER', 1); //Integer
+ define ('XMLDB_TYPE_NUMBER', 2); //Decimal number
+ define ('XMLDB_TYPE_FLOAT', 3); //Floating Point number
+ define ('XMLDB_TYPE_CHAR', 4); //String
+ define ('XMLDB_TYPE_TEXT', 5); //Text
+ define ('XMLDB_TYPE_BINARY', 6); //Binary
+ define ('XMLDB_TYPE_DATETIME', 7); //Datetime
+ define ('XMLDB_TYPE_TIMESTAMP', 8); //Timestamp
+
+/// Now the allowed DB Keys
+ define ('XMLDB_KEY_INCORRECT', 0); //Wrong DB Key
+ define ('XMLDB_KEY_PRIMARY', 1); //Primary Keys
+ define ('XMLDB_KEY_UNIQUE', 2); //Unique Keys
+ define ('XMLDB_KEY_FOREIGN', 3); //Foreign Keys
+ define ('XMLDB_KEY_CHECK', 4); //Check Constraints - NOT USED!
+ define ('XMLDB_KEY_FOREIGN_UNIQUE',5); //Foreign Key + Unique Key
+
+/// Now the allowed Statement Types
+ define ('XMLDB_STATEMENT_INCORRECT', 0); //Wrong Statement Type
+ define ('XMLDB_STATEMENT_INSERT', 1); //Insert Statements
+ define ('XMLDB_STATEMENT_UPDATE', 2); //Update Statements
+ define ('XMLDB_STATEMENT_DELETE', 3); //Delete Statements
+ define ('XMLDB_STATEMENT_CUSTOM', 4); //Custom Statements
+
+/// Some other useful Constants
+ define ('XMLDB_UNSIGNED', true); //If the field is going to be unsigned
+ define ('XMLDB_NOTNULL', true); //If the field is going to be not null
+ define ('XMLDB_SEQUENCE', true); //If the field is going to be a sequence
+ define ('XMLDB_ENUM', true); //If the field is going to be a enumeration of possible fields
+ define ('XMLDB_INDEX_UNIQUE', true); //If the index is going to be unique
+ define ('XMLDB_INDEX_NOTUNIQUE',false); //If the index is NOT going to be unique
+?>
--- /dev/null
+<?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 one XMLDB Field
+
+class XMLDBField extends XMLDBObject {
+
+ var $type;
+ var $length;
+ var $unsigned;
+ var $notnull;
+ var $default;
+ var $sequence;
+ var $enum;
+ var $enumvalues;
+ var $decimals;
+
+ /**
+ * Creates one new XMLDBField
+ */
+ function XMLDBField($name) {
+ parent::XMLDBObject($name);
+ $this->type = NULL;
+ $this->length = NULL;
+ $this->unsigned = false;
+ $this->notnull = false;
+ $this->default = NULL;
+ $this->sequence = false;
+ $this->enum = false;
+ $this->enumvalues = NULL;
+ $this->decimals = NULL;
+ }
+
+ /**
+ * Get the type
+ */
+ function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Get the length
+ */
+ function getLength() {
+ return $this->length;
+ }
+
+ /**
+ * Get the decimals
+ */
+ function getDecimals() {
+ return $this->decimals;
+ }
+
+ /**
+ * Get the notnull
+ */
+ function getNotNull() {
+ return $this->notnull;
+ }
+
+ /**
+ * Get the unsigned
+ */
+ function getUnsigned() {
+ return $this->unsigned;
+ }
+
+ /**
+ * Get the sequence
+ */
+ function getSequence() {
+ return $this->sequence;
+ }
+
+ /**
+ * Get the enum
+ */
+ function getEnum() {
+ return $this->enum;
+ }
+
+ /**
+ * Get the enumvalues
+ */
+ function getEnumValues() {
+ return $this->enumvalues;
+ }
+
+ /**
+ * Get the default
+ */
+ function getDefault() {
+ return $this->default;
+ }
+
+ /**
+ * Set the field type
+ */
+ function setType($type) {
+ $this->type = $type;
+ }
+
+ /**
+ * Set the field length
+ */
+ function setLength($length) {
+ $this->length = $length;
+ }
+
+ /**
+ * Set the field decimals
+ */
+ function setDecimals($decimals) {
+ $this->decimals = $decimals;
+ }
+
+ /**
+ * Set the field unsigned
+ */
+ function setUnsigned($unsigned=true) {
+ $this->unsigned = $unsigned;
+ }
+
+ /**
+ * Set the field notnull
+ */
+ function setNotNull($notnull=true) {
+ $this->notnull = $notnull;
+ }
+
+ /**
+ * Set the field sequence
+ */
+ function setSequence($sequence=true) {
+ $this->sequence = $sequence;
+ }
+
+ /**
+ * Set the field enum
+ */
+ function setEnum($enum=true) {
+ $this->enum = $enum;
+ }
+
+ /**
+ * Set the field enumvalues
+ */
+ function setEnumValues($enumvalues) {
+ $this->enumvalues = $enumvalues;
+ }
+
+ /**
+ * Set the field default
+ */
+ function setDefault($default) {
+ $this->default = $default;
+ }
+
+ /**
+ * Load data from XML to the table
+ */
+ function arr2XMLDBField($xmlarr) {
+
+ $result = true;
+
+ /// Debug the table
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process table attributes (name, type, length, unsigned,
+ /// notnull, sequence, enum, enumvalues, decimals, comment,
+ /// previous, next)
+ if (isset($xmlarr['@']['NAME'])) {
+ $this->name = trim($xmlarr['@']['NAME']);
+ } else {
+ $this->errormsg = 'Missing NAME attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['TYPE'])) {
+ /// Check for valid type
+ $type = $this->getXMLDBFieldType(trim($xmlarr['@']['TYPE']));
+ if ($type) {
+ $this->type = $type;
+ } else {
+ $this->errormsg = 'Invalid TYPE attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Missing TYPE attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['LENGTH'])) {
+ $length = trim($xmlarr['@']['LENGTH']);
+ /// Check for integer values
+ if ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_CHAR) {
+ if (!(is_numeric($length)&&(intval($length)==floatval($length)))) {
+ $this->errormsg = 'Incorrect LENGTH attribute for int, number or char fields';
+ $result = false;
+ } else if (!$length) {
+ $this->errormsg = 'Zero LENGTH attribute';
+ $result = false;
+ }
+ }
+ /// Check for big, medium, small to be applied to text and binary
+ if ($this->type == XMLDB_TYPE_TEXT ||
+ $this->type == XMLDB_TYPE_BINARY) {
+ if (!$length) {
+ $length == 'big';
+ }
+ if ($length != 'big' &&
+ $length != 'medium' &&
+ $length != 'small') {
+ $this->errormsg = 'Incorrect LENGTH attribute for text and binary fields (only big, medium and small allowed)';
+ $result = false;
+ }
+ }
+ /// Finally, set the length
+ $this->length = $length;
+ }
+
+ if (isset($xmlarr['@']['UNSIGNED'])) {
+ $unsigned = strtolower(trim($xmlarr['@']['UNSIGNED']));
+ if ($unsigned == 'true') {
+ $this->unsigned = true;
+ } else if ($unsigned == 'false') {
+ $this->unsigned = false;
+ } else {
+ $this->errormsg = 'Incorrect UNSIGNED attribute (true/false allowed)';
+ $result = false;
+ }
+ }
+
+ if (isset($xmlarr['@']['NOTNULL'])) {
+ $notnull = strtolower(trim($xmlarr['@']['NOTNULL']));
+ if ($notnull == 'true') {
+ $this->notnull = true;
+ } else if ($notnull == 'false') {
+ $this->notnull = false;
+ } else {
+ $this->errormsg = 'Incorrect NOTNULL attribute (true/false allowed)';
+ $result = false;
+ }
+ }
+
+ if (isset($xmlarr['@']['SEQUENCE'])) {
+ $sequence = strtolower(trim($xmlarr['@']['SEQUENCE']));
+ if ($sequence == 'true') {
+ $this->sequence = true;
+ } else if ($sequence == 'false') {
+ $this->sequence = false;
+ } else {
+ $this->errormsg = 'Incorrect SEQUENCE attribute (true/false allowed)';
+ $result = false;
+ }
+ }
+
+ if (isset($xmlarr['@']['DEFAULT'])) {
+ $this->default = trim($xmlarr['@']['DEFAULT']);
+ }
+
+ if (isset($xmlarr['@']['ENUM'])) {
+ $enum = strtolower(trim($xmlarr['@']['ENUM']));
+ if ($enum == 'true') {
+ $this->enum = true;
+ } else if ($enum == 'false') {
+ $this->enum = false;
+ } else {
+ $this->errormsg = 'Incorrect ENUM attribute (true/false allowed)';
+ $result = false;
+ }
+ }
+
+ if (isset($xmlarr['@']['ENUMVALUES'])) {
+ $enumvalues = strtolower(trim($xmlarr['@']['ENUMVALUES']));
+ if (!$this->enum) {
+ $this->errormsg = 'Wrong ENUMVALUES attribute (not ENUM)';
+ $result = false;
+ $this->enumvalues = $enumvalues;
+ } else {
+ /// Check we have a valid list (comma separated of quoted values)
+ $enumarr = explode(',',$enumvalues);
+ if ($enumarr) {
+ foreach ($enumarr as $key => $enumelement) {
+ /// Clear some spaces
+ $enumarr[$key] = trim($enumelement);
+ $enumelement = trim($enumelement);
+ /// Skip if under error
+ if (!$result) {
+ continue;
+ }
+ /// Look for quoted strings
+ if (substr($enumelement, 0, 1) != "'" ||
+ substr($enumelement, -1, 1) != "'") {
+ $this->errormsg = 'Incorrect ENUMVALUES attribute (some value is not properly quoted)';
+ $result = false;
+ }
+ }
+ } else {
+ $this->errormsg = 'Incorrect ENUMVALUES attribute (comma separated of quoted values)';
+ $result = false;
+ }
+ }
+ } else if ($this->enum) {
+ $this->errormsg = 'Incorrect ENUMVALUES attribute (field is not declared as ENUM)';
+ $result = false;
+ }
+ /// Finally, set the value
+ if ($this->enum) {
+ $this->enumvalues = $enumarr;
+ }
+
+ $decimals = NULL;
+ if (isset($xmlarr['@']['DECIMALS'])) {
+ $decimals = trim($xmlarr['@']['DECIMALS']);
+ /// Check for integer values
+ if ($this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT) {
+ if (!(is_numeric($decimals)&&(intval($decimals)==floatval($decimals)))) {
+ $this->errormsg = 'Incorrect DECIMALS attribute for number field';
+ $result = false;
+ } else if ($this->length <= $decimals){
+ $this->errormsg = 'Incorrect DECIMALS attribute (bigget than length)';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Incorrect DECIMALS attribute for non-number field';
+ $result = false;
+ }
+ } else {
+ if ($this->type == XMLDB_TYPE_NUMBER) {
+ $decimals = 0;
+ }
+ }
+ // Finally, set the decimals
+ if ($this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT) {
+ $this->decimals = $decimals;
+ }
+
+ if (isset($xmlarr['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['@']['COMMENT']);
+ }
+
+ if (isset($xmlarr['@']['PREVIOUS'])) {
+ $this->previous = trim($xmlarr['@']['PREVIOUS']);
+ }
+
+ if (isset($xmlarr['@']['NEXT'])) {
+ $this->next = trim($xmlarr['@']['NEXT']);
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function returns the correct XMLDB_TYPE_XXX value for the
+ * string passed as argument
+ */
+ function getXMLDBFieldType($type) {
+
+ $result = XMLDB_TYPE_INCORRECT;
+
+ switch (strtolower($type)) {
+ case 'int':
+ $result = XMLDB_TYPE_INTEGER;
+ break;
+ case 'number':
+ $result = XMLDB_TYPE_NUMBER;
+ break;
+ case 'float':
+ $result = XMLDB_TYPE_FLOAT;
+ break;
+ case 'char':
+ $result = XMLDB_TYPE_CHAR;
+ break;
+ case 'text':
+ $result = XMLDB_TYPE_TEXT;
+ break;
+ case 'binary':
+ $result = XMLDB_TYPE_BINARY;
+ break;
+ case 'datetime':
+ $result = XMLDB_TYPE_DATETIME;
+ break;
+ }
+ /// Return the normalized XMLDB_TYPE
+ return $result;
+ }
+
+ /**
+ * This function returns the correct name value for the
+ * XMLDB_TYPE_XXX passed as argument
+ */
+ function getXMLDBTypeName($type) {
+
+ $result = "";
+
+ switch (strtolower($type)) {
+ case XMLDB_TYPE_INTEGER:
+ $result = 'int';
+ break;
+ case XMLDB_TYPE_NUMBER:
+ $result = 'number';
+ break;
+ case XMLDB_TYPE_FLOAT:
+ $result = 'float';
+ break;
+ case XMLDB_TYPE_CHAR:
+ $result = 'char';
+ break;
+ case XMLDB_TYPE_TEXT:
+ $result = 'text';
+ break;
+ case XMLDB_TYPE_BINARY:
+ $result = 'binary';
+ break;
+ case XMLDB_TYPE_DATETIME:
+ $result = 'datetime';
+ break;
+ }
+ /// Return the normalized name
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBField
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->name . $this->type . $this->length .
+ $this->unsigned . $this->notnull . $this->sequence .
+ $this->decimals . $this->comment;
+ if ($this->enum) {
+ $key .= implode(', ',$this->enumvalues);
+ }
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ *This function will output the XML text for one field
+ */
+ function xmlOutput() {
+ $o = '';
+ $o.= ' <FIELD NAME="' . $this->name . '"';
+ $o.= ' TYPE="' . $this->getXMLDBTypeName($this->type) . '"';
+ if ($this->length) {
+ $o.= ' LENGTH="' . $this->length . '"';
+ }
+ if ($this->notnull) {
+ $notnull = 'true';
+ } else {
+ $notnull = 'false';
+ }
+ $o.= ' NOTNULL="' . $notnull . '"';
+ if ($this->unsigned) {
+ $unsigned = 'true';
+ } else {
+ $unsigned = 'false';
+ }
+ if ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT) {
+ if ($this->unsigned) {
+ $unsigned = 'true';
+ } else {
+ $unsigned = 'false';
+ }
+ $o.= ' UNSIGNED="' . $unsigned . '"';
+ }
+ if (!$this->sequence && $this->default !== NULL) {
+ $o.= ' DEFAULT="' . $this->default . '"';
+ }
+ if ($this->sequence) {
+ $sequence = 'true';
+ } else {
+ $sequence = 'false';
+ }
+ $o.= ' SEQUENCE="' . $sequence . '"';
+ if ($this->enum) {
+ $enum = 'true';
+ } else {
+ $enum = 'false';
+ }
+ $o.= ' ENUM="' . $enum . '"';
+ if ($this->enum) {
+ $o.= ' ENUMVALUES="' . implode(', ', $this->enumvalues) . '"';
+ }
+ if ($this->decimals !== NULL) {
+ $o.= ' DECIMALS="' . $this->decimals . '"';
+ }
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ if ($this->previous) {
+ $o.= ' PREVIOUS="' . $this->previous . '"';
+ }
+ if ($this->next) {
+ $o.= ' NEXT="' . $this->next . '"';
+ }
+ $o.= '/>' . "\n";
+
+ return $o;
+ }
+
+ /**
+ * This function will set all the attributes of the XMLDBField object
+ * based on information passed in one ADOField
+ */
+ function setFromADOField($adofield) {
+
+ /// Calculate the XMLDB_TYPE
+ switch (strtolower($adofield->type)) {
+ case 'int':
+ case 'tinyint':
+ case 'smallint':
+ case 'bigint':
+ case 'integer':
+ $this->type = XMLDB_TYPE_INTEGER;
+ break;
+ case 'number':
+ case 'decimal':
+ case 'dec':
+ case 'numeric':
+ $this->type = XMLDB_TYPE_NUMBER;
+ break;
+ case 'float':
+ case 'double':
+ $this->type = XMLDB_TYPE_FLOAT;
+ break;
+ case 'char':
+ case 'varchar':
+ case 'enum':
+ $this->type = XMLDB_TYPE_CHAR;
+ break;
+ case 'text':
+ case 'tinytext':
+ case 'mediumtext':
+ case 'longtext':
+ $this->type = XMLDB_TYPE_TEXT;
+ break;
+ case 'blob':
+ case 'tinyblob':
+ case 'mediumblob':
+ case 'longblob':
+ $this->type = XMLDB_TYPE_BINARY;
+ break;
+ case 'datetime':
+ case 'timestamp':
+ $this->type = XMLDB_TYPE_DATETIME;
+ break;
+ default:
+ $this->type = XMLDB_TYPE_TEXT;
+ }
+ /// Calculate the length of the field
+ if ($adofield->max_length > 0 &&
+ ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT ||
+ $this->type == XMLDB_TYPE_CHAR)) {
+ $this->length = $adofield->max_length;
+ }
+ if ($this->type == XMLDB_TYPE_TEXT) {
+ switch (strtolower($adofield->type)) {
+ case 'tinytext':
+ case 'text':
+ $this->length = 'small';
+ break;
+ case 'mediumtext':
+ $this->length = 'medium';
+ break;
+ case 'longtext':
+ $this->length = 'big';
+ break;
+ default:
+ $this->length = 'small';
+ }
+ }
+ if ($this->type == XMLDB_TYPE_BINARY) {
+ switch (strtolower($adofield->type)) {
+ case 'tinyblob':
+ case 'blob':
+ $this->length = 'small';
+ break;
+ case 'mediumblob':
+ $this->length = 'medium';
+ break;
+ case 'longblob':
+ $this->length = 'big';
+ break;
+ default:
+ $this->length = 'small';
+ }
+ }
+ /// Calculate the decimals of the field
+ if ($adofield->max_length > 0 &&
+ $adofield->scale &&
+ ($this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT)) {
+ $this->decimals = $adofield->scale;
+ }
+ /// Calculate the unsigned field
+ if ($adofield->unsigned &&
+ ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT)) {
+ $this->unsigned = true;
+ }
+ /// Calculate the notnull field
+ if ($adofield->not_null) {
+ $this->notnull = true;
+ }
+ /// Calculate the default field
+ if ($adofield->has_default) {
+ $this->default = $adofield->default_value;
+ }
+ /// Calculate the sequence field
+ if ($adofield->auto_increment) {
+ $this->sequence = true;
+ /// Sequence fields are always unsigned
+ $this->unsigned = true;
+ }
+ /// Calculate the enum and enumvalues field
+ if ($adofield->type == 'enum') {
+ $this->enum = true;
+ $this->enumvalues = $adofield->enums;
+ }
+ /// Some more fields
+ $this->loaded = true;
+ $this->changed = true;
+ }
+
+ /**
+ * Shows info in a readable format
+ */
+ function readableInfo() {
+ $o = '';
+ /// type
+ $o .= $this->getXMLDBTypeName($this->type);
+ /// length
+ if ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT ||
+ $this->type == XMLDB_TYPE_CHAR) {
+ if ($this->length) {
+ $o .= ' (' . $this->length;
+ if ($this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT) {
+ if ($this->decimals !== NULL) {
+ $o .= ', ' . $this->decimals;
+ }
+ }
+ $o .= ')';
+ }
+ }
+ if ($this->type == XMLDB_TYPE_TEXT ||
+ $this->type == XMLDB_TYPE_BINARY) {
+ $o .= ' (' . $this->length . ')';
+ }
+ /// enum
+ if ($this->enum) {
+ $o .= ' enum(' . implode(', ', $this->enumvalues) . ')';
+ }
+ /// unsigned
+ if ($this->type == XMLDB_TYPE_INTEGER ||
+ $this->type == XMLDB_TYPE_NUMBER ||
+ $this->type == XMLDB_TYPE_FLOAT) {
+ if ($this->unsigned) {
+ $o .= ' unsigned';
+ } else {
+ $o .= ' signed';
+ }
+ }
+ /// not null
+ if ($this->notnull) {
+ $o .= ' not null';
+ }
+ /// default
+ if ($this->default !== NULL) {
+ $o .= ' default ';
+ if ($this->type == XMLDB_TYPE_CHAR ||
+ $this->type == XMLDB_TYPE_TEXT) {
+ $o .= "'" . $this->default . "'";
+ } else {
+ $o .= $this->default;
+ }
+ }
+ /// sequence
+ if ($this->sequence) {
+ $o .= ' auto-numbered';
+ }
+
+ return $o;
+ }
+}
+
+?>
--- /dev/null
+<?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 represents an entire XMLDB file
+
+class XMLDBFile extends XMLDBObject {
+
+ var $path;
+ var $xmldb_structure;
+
+ /**
+ * Constructor of the XMLDBFile
+ */
+ function XMLDBFile ($path) {
+ parent::XMLDBObject($path);
+ $this->path = $path;
+ $this->xmldb_structure = NULL;
+ }
+
+ /**
+ * Determine if the XML file exists
+ */
+ function fileExists() {
+ if (file_exists($this->path) && is_readable($this->path)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Determine if the XML is writeable
+ */
+ function fileWriteable() {
+ if (is_writeable(dirname($this->path))) {
+ return true;
+ }
+ return false;
+ }
+
+ function &getStructure() {
+ return $this->xmldb_structure;
+ }
+
+ /**
+ * Load and the XMLDB structure from file
+ */
+ function loadXMLStructure() {
+ if ($this->fileExists()) {
+ /// File exists, so let's process it
+ /// Load everything to a big array
+ $xmlarr = xmlize(file_get_contents($this->path));
+ /// Convert array to xmldb structure
+ $this->xmldb_structure = $this->arr2XMLDBStructure($xmlarr);
+ /// Analize results
+ if ($this->xmldb_structure->isLoaded()) {
+ $this->loaded = true;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This function takes an xmlized array and put it into one XMLDBStructure
+ */
+ function arr2XMLDBStructure ($xmlarr) {
+ $structure = new XMLDBStructure($this->path);
+ $structure->arr2XMLDBStructure($xmlarr);
+ return $structure;
+ }
+
+ /**
+ * This function saves the whole XMLDBStructure to its file
+ */
+ function saveXMLFile() {
+
+ $result = true;
+
+ $structure =& $this->getStructure();
+
+ $result = file_put_contents($this->path, $structure->xmlOutput());
+
+ return $result;
+ }
+}
+
+?>
--- /dev/null
+<?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 one XMLDB Index
+
+class XMLDBIndex extends XMLDBObject {
+
+ var $unique;
+ var $fields;
+
+ /**
+ * Creates one new XMLDBIndex
+ */
+ function XMLDBIndex($name) {
+ parent::XMLDBObject($name);
+ $this->unique = false;
+ $this->fields = array();
+ }
+
+ /**
+ * Get the index unique
+ */
+ function getUnique() {
+ return $this->unique;
+ }
+
+ /**
+ * Set the index unique
+ */
+ function setUnique($unique = true) {
+ $this->unique = $unique;
+ }
+
+ /**
+ * Set the index fields
+ */
+ function setFields($fields) {
+ $this->fields = $fields;
+ }
+
+ /**
+ * Get the index fields
+ */
+ function &getFields() {
+ return $this->fields;
+ }
+
+ /**
+ * Load data from XML to the index
+ */
+ function arr2XMLDBIndex($xmlarr) {
+
+ $result = true;
+
+ /// Debug the table
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process key attributes (name, unique, fields, comment, previous, next)
+ if (isset($xmlarr['@']['NAME'])) {
+ $this->name = trim($xmlarr['@']['NAME']);
+ } else {
+ $this->errormsg = 'Missing NAME attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['UNIQUE'])) {
+ $unique = strtolower(trim($xmlarr['@']['UNIQUE']));
+ if ($unique == 'true') {
+ $this->unique = true;
+ } else if ($unique == 'false') {
+ $this->unique = false;
+ } else {
+ $this->errormsg = 'Incorrect UNIQUE attribute (true/false allowed)';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Undefined UNIQUE attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['FIELDS'])) {
+ $fields = strtolower(trim($xmlarr['@']['FIELDS']));
+ if ($fields) {
+ $fieldsarr = explode(',',$fields);
+ if ($fieldsarr) {
+ foreach ($fieldsarr as $key => $element) {
+ $fieldsarr [$key] = trim($element);
+ }
+ } else {
+ $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Empty FIELDS attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Missing FIELDS attribute';
+ $result = false;
+ }
+ /// Finally, set the array of fields
+ $this->fields = $fieldsarr;
+
+ if (isset($xmlarr['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['@']['COMMENT']);
+ }
+
+ if (isset($xmlarr['@']['PREVIOUS'])) {
+ $this->previous = trim($xmlarr['@']['PREVIOUS']);
+ }
+
+ if (isset($xmlarr['@']['NEXT'])) {
+ $this->next = trim($xmlarr['@']['NEXT']);
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBIndex
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->unique . implode (', ', $this->fields);
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ *This function will output the XML text for one index
+ */
+ function xmlOutput() {
+ $o = '';
+ $o.= ' <INDEX NAME="' . $this->name . '"';
+ if ($this->unique) {
+ $unique = 'true';
+ } else {
+ $unique = 'false';
+ }
+ $o.= ' UNIQUE="' . $unique . '"';
+ $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ if ($this->previous) {
+ $o.= ' PREVIOUS="' . $this->previous . '"';
+ }
+ if ($this->next) {
+ $o.= ' NEXT="' . $this->next . '"';
+ }
+ $o.= '/>' . "\n";
+
+ return $o;
+ }
+
+ /**
+ * This function will set all the attributes of the XMLDBIndex object
+ * based on information passed in one ADOindex
+ */
+ function setFromADOIndex($adoindex) {
+
+ /// Set the unique field
+ $this->unique = false;
+ /// Set the fields
+ $this->fields = $adoindex['columns'];
+ /// Some more fields
+ $this->loaded = true;
+ $this->changed = true;
+ }
+
+ /**
+ * Shows info in a readable format
+ */
+ function readableInfo() {
+ $o = '';
+ /// unique
+ if ($this->unique) {
+ $o .= 'unique';
+ } else {
+ $o .= 'not unique';
+ }
+ /// fields
+ $o .= ' (' . implode(', ', $this->fields) . ')';
+
+ return $o;
+ }
+}
+
+?>
--- /dev/null
+<?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 one XMLDB Key
+
+class XMLDBKey extends XMLDBObject {
+
+ var $type;
+ var $fields;
+ var $reftable;
+ var $reffields;
+
+ /**
+ * Creates one new XMLDBKey
+ */
+ function XMLDBKey($name) {
+ parent::XMLDBObject($name);
+ $this->type = NULL;
+ $this->fields = array();
+ $this->reftable = NULL;
+ $this->reffields = array();
+ }
+
+ /**
+ * Get the key type
+ */
+ function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Set the key type
+ */
+ function setType($type) {
+ $this->type = $type;
+ }
+
+ /**
+ * Set the key fields
+ */
+ function setFields($fields) {
+ $this->fields = $fields;
+ }
+
+ /**
+ * Set the key reftable
+ */
+ function setRefTable($reftable) {
+ $this->reftable = $reftable;
+ }
+
+ /**
+ * Set the key reffields
+ */
+ function setRefFields($reffields) {
+ $this->reffields = $reffields;
+ }
+
+ /**
+ * Get the key fields
+ */
+ function &getFields() {
+ return $this->fields;
+ }
+
+ /**
+ * Get the key reftable
+ */
+ function &getRefTable() {
+ return $this->reftable;
+ }
+
+ /**
+ * Get the key reffields
+ */
+ function &getRefFields() {
+ return $this->reffields;
+ }
+
+ /**
+ * Load data from XML to the key
+ */
+ function arr2XMLDBKey($xmlarr) {
+
+ $result = true;
+
+ /// Debug the table
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process key attributes (name, type, fields, reftable,
+ /// reffields, comment, previous, next)
+ if (isset($xmlarr['@']['NAME'])) {
+ $this->name = trim($xmlarr['@']['NAME']);
+ } else {
+ $this->errormsg = 'Missing NAME attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['TYPE'])) {
+ /// Check for valid type
+ $type = $this->getXMLDBKeyType(trim($xmlarr['@']['TYPE']));
+ if ($type) {
+ $this->type = $type;
+ } else {
+ $this->errormsg = 'Invalid TYPE attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Missing TYPE attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['FIELDS'])) {
+ $fields = strtolower(trim($xmlarr['@']['FIELDS']));
+ if ($fields) {
+ $fieldsarr = explode(',',$fields);
+ if ($fieldsarr) {
+ foreach ($fieldsarr as $key => $element) {
+ $fieldsarr [$key] = trim($element);
+ }
+ } else {
+ $this->errormsg = 'Incorrect FIELDS attribute (comma separated of fields)';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Empty FIELDS attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Missing FIELDS attribute';
+ $result = false;
+ }
+ /// Finally, set the array of fields
+ $this->fields = $fieldsarr;
+
+ if (isset($xmlarr['@']['REFTABLE'])) {
+ /// Check we are in a FK
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $reftable = strtolower(trim($xmlarr['@']['REFTABLE']));
+ if (!$reftable) {
+ $this->errormsg = 'Empty REFTABLE attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Wrong REFTABLE attribute (only FK can have it)';
+ $result = false;
+ }
+ } else if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $this->errormsg = 'Missing REFTABLE attribute';
+ $result = false;
+ }
+ /// Finally, set the reftable
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $this->reftable = $reftable;
+ }
+
+ if (isset($xmlarr['@']['REFFIELDS'])) {
+ /// Check we are in a FK
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $reffields = strtolower(trim($xmlarr['@']['REFFIELDS']));
+ if ($reffields) {
+ $reffieldsarr = explode(',',$reffields);
+ if ($reffieldsarr) {
+ foreach ($reffieldsarr as $key => $element) {
+ $reffieldsarr [$key] = trim($element);
+ }
+ } else {
+ $this->errormsg = 'Incorrect REFFIELDS attribute (comma separated of fields)';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Empty REFFIELDS attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Wrong REFFIELDS attribute (only FK can have it)';
+ $result = false;
+ }
+ } else if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $this->errormsg = 'Missing REFFIELDS attribute';
+ $result = false;
+ }
+ /// Finally, set the array of reffields
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $this->reffields = $reffieldsarr;
+ }
+
+ if (isset($xmlarr['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['@']['COMMENT']);
+ }
+
+ if (isset($xmlarr['@']['PREVIOUS'])) {
+ $this->previous = trim($xmlarr['@']['PREVIOUS']);
+ }
+
+ if (isset($xmlarr['@']['NEXT'])) {
+ $this->next = trim($xmlarr['@']['NEXT']);
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function returns the correct XMLDB_KEY_XXX value for the
+ * string passed as argument
+ */
+ function getXMLDBKeyType($type) {
+
+ $result = XMLDB_KEY_INCORRECT;
+
+ switch (strtolower($type)) {
+ case 'primary':
+ $result = XMLDB_KEY_PRIMARY;
+ break;
+ case 'unique':
+ $result = XMLDB_KEY_UNIQUE;
+ break;
+ case 'foreign':
+ $result = XMLDB_KEY_FOREIGN;
+ break;
+ case 'foreign-unique':
+ $result = XMLDB_KEY_FOREIGN_UNIQUE;
+ break;
+ /// case 'check': //Not supported
+ /// $result = XMLDB_KEY_CHECK;
+ /// break;
+ }
+ /// Return the normalized XMLDB_KEY
+ return $result;
+ }
+
+ /**
+ * This function returns the correct name value for the
+ * XMLDB_KEY_XXX passed as argument
+ */
+ function getXMLDBKeyName($type) {
+
+ $result = '';
+
+ switch (strtolower($type)) {
+ case XMLDB_KEY_PRIMARY:
+ $result = 'primary';
+ break;
+ case XMLDB_KEY_UNIQUE:
+ $result = 'unique';
+ break;
+ case XMLDB_KEY_FOREIGN:
+ $result = 'foreign';
+ break;
+ case XMLDB_KEY_FOREIGN_UNIQUE:
+ $result = 'foreign-unique';
+ break;
+ /// case XMLDB_KEY_CHECK: //Not supported
+ /// $result = 'check';
+ /// break;
+ }
+ /// Return the normalized name
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBKey
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->type . implode(', ', $this->fields);
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $key .= $this->reftable . implode(', ', $this->reffields);
+ }
+ ;
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ *This function will output the XML text for one key
+ */
+ function xmlOutput() {
+ $o = '';
+ $o.= ' <KEY NAME="' . $this->name . '"';
+ $o.= ' TYPE="' . $this->getXMLDBKeyName($this->type) . '"';
+ $o.= ' FIELDS="' . implode(', ', $this->fields) . '"';
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $o.= ' REFTABLE="' . $this->reftable . '"';
+ $o.= ' REFFIELDS="' . implode(', ', $this->reffields) . '"';
+ }
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ if ($this->previous) {
+ $o.= ' PREVIOUS="' . $this->previous . '"';
+ }
+ if ($this->next) {
+ $o.= ' NEXT="' . $this->next . '"';
+ }
+ $o.= '/>' . "\n";
+
+ return $o;
+ }
+
+ /**
+ * This function will set all the attributes of the XMLDBKey object
+ * based on information passed in one ADOkey
+ */
+ function setFromADOKey($adokey) {
+
+ /// Calculate the XMLDB_KEY
+ switch (strtolower($adokey['name'])) {
+ case 'primary':
+ $this->type = XMLDB_KEY_PRIMARY;
+ break;
+ default:
+ $this->type = XMLDB_KEY_UNIQUE;
+ }
+ /// Set the fields
+ $this->fields = $adokey['columns'];
+ /// Some more fields
+ $this->loaded = true;
+ $this->changed = true;
+ }
+
+ /**
+ * Shows info in a readable format
+ */
+ function readableInfo() {
+ $o = '';
+ /// type
+ $o .= $this->getXMLDBKeyName($this->type);
+ /// fields
+ $o .= ' (' . implode(', ', $this->fields) . ')';
+ /// foreign key
+ if ($this->type == XMLDB_KEY_FOREIGN ||
+ $this->type == XMLDB_KEY_FOREIGN_UNIQUE) {
+ $o .= ' references ' . $this->reftable . ' (' . implode(', ', $this->reffields) . ')';
+ }
+
+ return $o;
+ }
+}
+
+?>
--- /dev/null
+<?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 XMLDB base class where all the common piezes
+/// are defined
+
+class XMLDBObject {
+
+ var $name;
+ var $comment;
+ var $previous;
+ var $next;
+ var $hash;
+ var $loaded;
+ var $changed;
+ var $errormsg;
+
+ /**
+ * Creates one new XMLDBObject
+ */
+ function XMLDBObject($name) {
+ $this->name = $name;
+ $this->comment = NULL;
+ $this->previous = NULL;
+ $this->next = NULL;
+ $this->hash = NULL;
+ $this->loaded = false;
+ $this->changed = false;
+ $this->errormsg = NULL;
+ }
+
+ /**
+ * This function returns true/false, if the XMLDBObject has been loaded
+ */
+ function isLoaded() {
+ return $this->loaded;
+ }
+
+ /**
+ * This function returns true/false, if the XMLDBObject has changed
+ */
+ function hasChanged() {
+ return $this->changed;
+ }
+
+ /**
+ * This function returns the comment of one XMLDBObject
+ */
+ function getComment() {
+ return $this->comment;
+ }
+
+ /**
+ * This function returns the hash of one XMLDBObject
+ */
+ function getHash() {
+ return $this->hash;
+ }
+
+ /**
+ * This function will return the name of the previous XMLDBObject
+ */
+ function getPrevious() {
+ return $this->previous;
+ }
+
+ /**
+ * This function will return the name of the next XMLDBObject
+ */
+ function getNext() {
+ return $this->next;
+ }
+
+ /**
+ * This function will return the name of the XMLDBObject
+ */
+ function getName() {
+ return $this->name;
+ }
+
+ /**
+ * This function will return the error detected in the object
+ */
+ function getError() {
+ return $this->errormsg;
+ }
+
+ /**
+ * This function will set the comment of the XMLDB object
+ */
+ function setComment($comment) {
+ $this->comment = $comment;
+ }
+
+ /**
+ * This function will set the previous of the XMLDB object
+ */
+ function setPrevious($previous) {
+ $this->previous = $previous;
+ }
+
+ /**
+ * This function will set the next of the XMLDB object
+ */
+ function setNext($next) {
+ $this->next = $next;
+ }
+
+ /**
+ * This function will set the hash of the XMLDB object
+ */
+ function setHash($hash) {
+ $this->hash = $hash;
+ }
+
+ /**
+ * This function will set the loaded field of the XMLDB object
+ */
+ function setLoaded($loaded = true) {
+ $this->loaded = $loaded;
+ }
+
+ /**
+ * This function will set the changed field of the XMLDB object
+ */
+ function setChanged($changed = true) {
+ $this->changed = $changed;
+ }
+ /**
+ * This function will set the name field of the XMLDB object
+ */
+ function setName($name) {
+ $this->name = $name;
+ }
+
+
+ /**
+ * This function will check if one key name is ok or no (true/false)
+ * only lowercase a-z, 0-9 and _ are allowed
+ */
+ function checkName () {
+ $result = true;
+
+ if ($this->name != eregi_replace('[^a-z0-9_ -]', '', $this->name)) {
+ $result = false;
+ }
+ return $result;
+ }
+
+ /**
+ * This function will check that all the elements in one array
+ * have a correct name [a-z0-9_]
+ */
+ function checkNameValues(&$arr) {
+ $result = true;
+ /// TODO: Perhaps, add support for reserved words
+
+ /// Check the name only contains valid chars
+ if ($arr) {
+ foreach($arr as $element) {
+ if (!$element->checkName()) {
+ $result = false;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * This function will check that all the elements in one array
+ * have a consistent info in their previous/next fields
+ */
+ function checkPreviousNextValues(&$arr) {
+ $result = true;
+ /// Check that only one element has the previous not set
+ if ($arr) {
+ $counter = 0;
+ foreach($arr as $element) {
+ if (!$element->getPrevious()) {
+ $counter++;
+ }
+ }
+ if ($counter != 1) {
+ $result = false;
+ }
+ }
+ /// Check that only one element has the next not set
+ if ($result && $arr) {
+ $counter = 0;
+ foreach($arr as $element) {
+ if (!$element->getNext()) {
+ $counter++;
+ }
+ }
+ if ($counter != 1) {
+ $result = false;
+ }
+ }
+ /// Check that all the previous elements are existing elements
+ if ($result && $arr) {
+ foreach($arr as $element) {
+ if ($element->getPrevious()) {
+ $i = $this->findObjectInArray($element->getPrevious(), $arr);
+ if ($i === NULL) {
+ $result = false;
+ }
+ }
+ }
+ }
+ /// Check that all the next elements are existing elements
+ if ($result && $arr) {
+ foreach($arr as $element) {
+ if ($element->getNext()) {
+ $i = $this->findObjectInArray($element->getNext(), $arr);
+ if ($i === NULL) {
+ $result = false;
+ }
+ }
+ }
+ }
+ /// Check that there aren't duplicates in the previous values
+ if ($result && $arr) {
+ $existarr = array();
+ foreach($arr as $element) {
+ if (in_array($element->getPrevious(), $existarr)) {
+ $result = false;
+ } else {
+ $existarr[] = $element->getPrevious();
+ }
+ }
+ }
+ /// Check that there aren't duplicates in the next values
+ if ($result && $arr) {
+ $existarr = array();
+ foreach($arr as $element) {
+ if (in_array($element->getNext(), $existarr)) {
+ $result = false;
+ } else {
+ $existarr[] = $element->getNext();
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * This function will order all the elements in one array, following
+ * the previous/next rules
+ */
+ function orderElements($arr) {
+ $result = true;
+ /// Create a new array
+ $newarr = array();
+ if (!empty($arr)) {
+ $currentelement = NULL;
+ /// Get the element without previous
+ foreach($arr as $key => $element) {
+ if (!$element->getPrevious()) {
+ $currentelement = $arr[$key];
+ $newarr[0] = $arr[$key];
+ }
+ }
+ if (!$currentelement) {
+ $result = false;
+ }
+ /// Follow the next rules
+ $counter = 1;
+ while ($result && $currentelement->getNext()) {
+ $i = $this->findObjectInArray($currentelement->getNext(), $arr);
+ $currentelement = $arr[$i];
+ $newarr[$counter] = $arr[$i];
+ $counter++;
+ }
+ /// Compare number of elements between original and new array
+ if ($result && count($arr) != count($newarr)) {
+ $result = false;
+ }
+ /// Check that previous/next is ok (redundant but...)
+ if ($this->checkPreviousNextValues($newarr)) {
+ $result = $newarr;
+ } else {
+ $result = false;
+ }
+ } else {
+ $result = array();
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the position of one object in the array.
+ */
+ function &findObjectInArray($objectname, $arr) {
+ foreach ($arr as $i => $object) {
+ if ($objectname == $object->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * This function will display a readable info about the XMLDBObject
+ * (should be implemented inside each XMLDBxxx object)
+ */
+ function readableInfo() {
+ return get_class($this);
+ }
+
+ /**
+ * Returns one array of elements from one comma separated string,
+ * supporting quoted strings containing commas and concat function calls
+ */
+ function comma2array($string) {
+
+ $arr = array();
+
+ $foundquotes = array();
+ $foundconcats = array();
+
+ /// Extract all the concat elements from the string
+ preg_match_all("/(CONCAT\(.*?\))/is", $string, $matches);
+ foreach (array_unique($matches[0]) as $key=>$value) {
+ $foundconcats['<#'.$key.'#>'] = $value;
+ }
+ if (!empty($foundconcats)) {
+ $string = str_replace($foundconcats,array_keys($foundconcats),$string);
+ }
+
+ /// Extract all the quoted elements from the string
+ preg_match_all("/('.*?')/is", $string, $matches);
+ foreach (array_unique($matches[0]) as $key=>$value) {
+ $foundquotes['<%'.$key.'%>'] = $value;
+ }
+ if (!empty($foundquotes)) {
+ $string = str_replace($foundquotes,array_keys($foundquotes),$string);
+ }
+
+ /// Explode safely the string
+ $arr = explode (',', $string);
+
+ /// Put the concat and quoted elements back again, triming every element
+ if ($arr) {
+ foreach ($arr as $key => $element) {
+ /// Clear some spaces
+ $element = trim($element);
+ /// Replace the quoted elements if exists
+ if (!empty($foundquotes)) {
+ $element = str_replace(array_keys($foundquotes), $foundquotes, $element);
+ }
+ /// Replace the concat elements if exists
+ if (!empty($foundconcats)) {
+ $element = str_replace(array_keys($foundconcats), $foundconcats, $element);
+ }
+ $arr[$key] = $element;
+ }
+ }
+
+ return $arr;
+ }
+}
+
+?>
--- /dev/null
+<?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 one XMLDB Statement
+/// (a group of SQL arbitrary sentences)
+/// (only INSERT is allowed for now)
+
+class XMLDBStatement extends XMLDBObject {
+
+ var $table; // Table we are handling
+ var $type; // XMLDB_STATEMENT_TYPE
+ var $sentences; // Collection of sentences in the statement
+
+ /**
+ * Creates one new XMLDBStatement
+ */
+ function XMLDBStatement($name) {
+ parent::XMLDBObject($name);
+ $this->table = NULL;
+ $this->type = XMLDB_STATEMENT_INCORRECT;
+ $this->sentences = array();
+ }
+
+ /**
+ * Get the statement table
+ */
+ function getTable() {
+ return $this->table;
+ }
+
+ /**
+ * Get the statement type
+ */
+ function getType() {
+ return $this->type;
+ }
+
+ /**
+ * Get the statement sentences
+ */
+ function &getSentences() {
+ return $this->sentences;
+ }
+
+ /**
+ * Set the statement table
+ */
+ function setTable($table) {
+ $this->table = $table;
+ }
+
+ /**
+ * Set the statement type
+ */
+ function setType($type) {
+ $this->type = $type;
+ }
+
+ /**
+ * Add one statement sentence
+ */
+ function addSentence($sentence) {
+ $this->sentences[] = $sentence;
+ }
+
+ /**
+ * Load data from XML to the index
+ */
+ function arr2XMLDBStatement($xmlarr) {
+
+ $result = true;
+
+ /// Debug the table
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process key attributes (table, type, comment, previous, next)
+ if (isset($xmlarr['@']['TABLE'])) {
+ $this->table = strtolower(trim($xmlarr['@']['TABLE']));
+ } else {
+ $this->errormsg = 'Missing TABLE attribute';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['TYPE'])) {
+ /// Check for valid type
+ $type = $this->getXMLDBStatementType(trim($xmlarr['@']['TYPE']));
+ if ($type) {
+ $this->type = $type;
+ } else {
+ $this->errormsg = 'Invalid TYPE attribute';
+ $result = false;
+ }
+ } else {
+ $this->errormsg = 'Missing TYPE attribute';
+ $result = false;
+ }
+
+ /// Look for sentences
+ $sentencesarr = array();
+
+ if (isset($xmlarr['#']['SENTENCES'])) {
+ $sentences = $xmlarr['#']['SENTENCES'][0]['#']['SENTENCE'];
+ if ($sentences) {
+ foreach ($sentences as $sentence) {
+ if (isset($sentence['@']['TEXT'])) {
+ $sentencesarr[] = trim($sentence['@']['TEXT']);
+ } else {
+ $this->errormsg = 'Missing TEXT attribute in sentence';
+ $result = false;
+ }
+ }
+ }
+ }
+
+ /// Finally, set the array of sentences
+ $this->sentences = $sentencesarr;
+
+ /// Now, perform some validations over sentences
+ /// XMLDB_STATEMENT_INSERT checks
+ if ($this->type == XMLDB_STATEMENT_INSERT) {
+ /// Separate fields and values into two arrays
+ if ($this->sentences) {
+ foreach ($this->sentences as $sentence) {
+ $fields = $this->getFieldsFromInsertSentence($sentence);
+ $values = $this->getValuesFromInsertSentence($sentence);
+ /// Check that we aren't inserting the id field
+ if (in_array('id', $fields)) {
+ $this->errormsg = 'Cannot insert the "id" field. It is an autonumeric column';
+ $result = false;
+ }
+ if ($result && count($fields) == 0) {
+ $this->errormsg = 'Missing fields in sentence "' . $sentence . '"';
+ $result = false;
+ }
+ if ($result && count($values) == 0) {
+ $this->errormsg = 'Missing values in sentence "' . $sentence . '"';
+ $result = false;
+ }
+ if ($result && count($fields) != count($values)) {
+ $this->errormsg = 'Incorrect number of fields (' .implode(', ', $fields) . ') or values (' . implode(', ', $values) . ')';
+ $result = false;
+ }
+ }
+ }
+ } else {
+ /// Sentences different from INSERT are not valid for now
+ $this->errormsg = 'Only INSERT statements are supported';
+ $result = false;
+ }
+
+ if (isset($xmlarr['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['@']['COMMENT']);
+ }
+
+ if (isset($xmlarr['@']['PREVIOUS'])) {
+ $this->previous = trim($xmlarr['@']['PREVIOUS']);
+ }
+
+ if (isset($xmlarr['@']['NEXT'])) {
+ $this->next = trim($xmlarr['@']['NEXT']);
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function returns the correct XMLDB_STATEMENT_XXX value for the
+ * string passed as argument
+ */
+ function getXMLDBStatementType($type) {
+
+ $result = XMLDB_STATEMENT_INCORRECT;
+
+ switch (strtolower($type)) {
+ case 'insert':
+ $result = XMLDB_STATEMENT_INSERT;
+ break;
+ case 'update':
+ $result = XMLDB_STATEMENT_UPDATE;
+ break;
+ case 'delete':
+ $result = XMLDB_STATEMENT_DELETE;
+ break;
+ case 'custom':
+ $result = XMLDB_STATEMENT_CUSTOM;
+ break;
+ }
+ /// Return the normalized XMLDB_STATEMENT
+ return $result;
+ }
+
+ /**
+ * This function returns the correct name value for the
+ * XMLDB_STATEMENT_XXX passed as argument
+ */
+ function getXMLDBStatementName($type) {
+
+ $result = '';
+
+ switch (strtolower($type)) {
+ case XMLDB_STATEMENT_INSERT:
+ $result = 'insert';
+ break;
+ case XMLDB_STATEMENT_UPDATE:
+ $result = 'update';
+ break;
+ case XMLDB_STATEMENT_DELETE:
+ $result = 'delete';
+ break;
+ case XMLDB_STATEMENT_CUSTOM:
+ $result = 'custom';
+ break;
+ }
+ /// Return the normalized name
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBStatement
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->table . $this->type . implode (', ', $this->sentences);
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ * This function will output the XML text for one statement
+ */
+ function xmlOutput() {
+ $o = '';
+ $o.= ' <STATEMENT NAME="' . $this->name . '" TYPE="' . XMLDBStatement::getXMLDBStatementName($this->type) . '" TABLE="' . $this->table . '"';
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ if ($this->previous) {
+ $o.= ' PREVIOUS="' . $this->previous . '"';
+ }
+ if ($this->next) {
+ $o.= ' NEXT="' . $this->next . '"';
+ }
+ if ($this->sentences) {
+ $o.= '>' . "\n";
+ $o.= ' <SENTENCES>' . "\n";
+ foreach ($this->sentences as $sentence) {
+ $o.= ' <SENTENCE TEXT="' . htmlspecialchars($sentence) . '" />' . "\n";
+ }
+ $o.= ' </SENTENCES>' . "\n";
+ $o.= ' </STATEMENT>' . "\n";
+ } else {
+ $o.= '/>' . "\n";
+ }
+
+ return $o;
+ }
+
+ /**
+ * This function will set all the attributes of the XMLDBIndex object
+ * based on information passed in one ADOindex
+ */
+ function setFromADOIndex($adoindex) {
+
+ /// Set the unique field
+ $this->unique = false;
+ /// Set the fields
+ $this->fields = $adoindex['columns'];
+ /// Some more fields
+ $this->loaded = true;
+ $this->changed = true;
+ }
+
+ /**
+ * Shows info in a readable format
+ */
+ function readableInfo() {
+ $o = '';
+ /// unique
+ if ($this->unique) {
+ $o .= 'unique';
+ } else {
+ $o .= 'not unique';
+ }
+ /// fields
+ $o .= ' (' . implode(', ', $this->fields) . ')';
+
+ return $o;
+ }
+
+ /**
+ * This function will return an array of fields from one INSERT sentence
+ */
+ function getFieldsFromInsertSentence($sentence) {
+
+ $fields = array();
+
+ /// Get first part from the sentence (before VALUES)
+ preg_match('/^\((.*)\)\s+VALUES/is', $sentence, $matches);
+ if (isset($matches[1])) {
+ $part = $matches[1];
+ /// Convert the comma separated string to an array
+ $arr = $this->comma2array($part);
+ if ($arr) {
+ $fields = $arr;
+ }
+ }
+
+ return $fields;
+ }
+
+ /**
+ * This function will return an array of values from one INSERT sentence
+ */
+ function getValuesFromInsertSentence($sentence) {
+
+ $values = array();
+
+ /// Get second part from the sentence (after VALUES)
+ preg_match('/VALUES\s+\((.*)\)$/is', $sentence, $matches);
+ if (isset($matches[1])) {
+ $part = $matches[1];
+ /// Convert the comma separated string to an array
+ $arr = $this->comma2array($part);
+ if ($arr) {
+ $values = $arr;
+ }
+ }
+
+ return $values;
+ }
+}
+
+?>
--- /dev/null
+<?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 one XMLDB structure
+
+class XMLDBStructure extends XMLDBObject {
+
+ var $path;
+ var $version;
+ var $tables;
+ var $statements;
+
+ /**
+ * Creates one new XMLDBStructure
+ */
+ function XMLDBStructure($name) {
+ parent::XMLDBObject($name);
+ $this->path = NULL;
+ $this->version = NULL;
+ $this->tables = array();
+ $this->statements = array();
+ }
+
+ /**
+ * Returns the path of the structure
+ */
+ function getPath() {
+ return $this->path;
+ }
+
+ /**
+ * Returns the version of the structure
+ */
+ function getVersion() {
+ return $this->version;
+ }
+
+ /**
+ * Returns one XMLDBTable
+ */
+ function &getTable($tablename) {
+ $i = $this->findTableInArray($tablename);
+ if ($i !== NULL) {
+ return $this->tables[$i];
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the position of one table in the array.
+ */
+ function &findTableInArray($tablename) {
+ foreach ($this->tables as $i => $table) {
+ if ($tablename == $table->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the position of one statement in the array.
+ */
+ function &findStatementInArray($statementname) {
+ foreach ($this->statements as $i => $statement) {
+ if ($statementname == $statement->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * This function will reorder the array of tables
+ */
+ function orderTables() {
+ $result =& $this->orderElements($this->tables);
+ if ($result) {
+ $this->setTables($result);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function will reorder the array of statements
+ */
+ function orderStatements() {
+ $result =& $this->orderElements($this->statements);
+ if ($result) {
+ $this->setStatements($result);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the tables of the structure
+ */
+ function &getTables() {
+ return $this->tables;
+ }
+
+ /**
+ * Returns one XMLDBStatement
+ */
+ function &getStatement($statementname) {
+ $i = $this->findStatementInArray($statementname);
+ if ($i !== NULL) {
+ return $this->statements[$i];
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the statements of the structure
+ */
+ function &getStatements() {
+ return $this->statements;
+ }
+
+ /**
+ * Set the structure version
+ */
+ function setVersion($version) {
+ $this->version = $version;
+ }
+
+ /**
+ * Add one table to the structure, allowing to specify the desired order
+ * If it's not specified, then the table is added at the end.
+ */
+ function addTable(&$table, $after=NULL) {
+
+ /// Calculate the previous and next tables
+ $prevtable = NULL;
+ $nexttable = NULL;
+
+ if (!$after) {
+ $alltables =& $this->getTables();
+ if ($alltables) {
+ end($alltables);
+ $prevtable =& $alltables[key($alltables)];
+ }
+ } else {
+ $prevtable =& $this->getTable($after);
+ }
+ if ($prevtable && $prevtable->getNext()) {
+ $nexttable =& $this->getTable($prevtable->getNext());
+ }
+
+ /// Set current table previous and next attributes
+ if ($prevtable) {
+ $table->setPrevious($prevtable->getName());
+ $prevtable->setNext($table->getName());
+ }
+ if ($nexttable) {
+ $table->setNext($nexttable->getName());
+ $nexttable->setPrevious($table->getName());
+ }
+ /// Some more attributes
+ $table->setLoaded(true);
+ $table->setChanged(true);
+ /// Add the new table
+ $this->tables[] =& $table;
+ /// Reorder the whole structure
+ $this->orderTables($this->tables);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one new table, so the structure has changed
+ $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+ $this->setChanged(true);
+ }
+
+ /**
+ * Add one statement to the structure, allowing to specify the desired order
+ * If it's not specified, then the statement is added at the end.
+ */
+ function addStatement(&$statement, $after=NULL) {
+
+ /// Calculate the previous and next tables
+ $prevstatement = NULL;
+ $nextstatement = NULL;
+
+ if (!$after) {
+ $allstatements =& $this->getStatements();
+ if ($allstatements) {
+ end($allstatements);
+ $prevstatement =& $allstatements[key($allstatements)];
+ }
+ } else {
+ $prevstatement =& $this->getStatement($after);
+ }
+ if ($prevstatement && $prevstatement->getNext()) {
+ $nextstatement =& $this->getStatement($prevstatement->getNext());
+ }
+
+ /// Set current statement previous and next attributes
+ if ($prevstatement) {
+ $statement->setPrevious($prevstatement->getName());
+ $prevstatement->setNext($statement->getName());
+ }
+ if ($nextstatement) {
+ $statement->setNext($nextstatement->getName());
+ $nextstatement->setPrevious($statement->getName());
+ }
+ /// Some more attributes
+ $statement->setLoaded(true);
+ $statement->setChanged(true);
+ /// Add the new statement
+ $this->statements[] =& $statement;
+ /// Reorder the whole structure
+ $this->orderStatements($this->statements);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one new statement, so the structure has changed
+ $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+ $this->setChanged(true);
+ }
+
+ /**
+ * Delete one table from the Structure
+ */
+ function deleteTable($tablename) {
+
+ $table =& $this->getTable($tablename);
+ if ($table) {
+ $i = $this->findTableInArray($tablename);
+ $prevtable = NULL;
+ $nexttable = NULL;
+ /// Look for prev and next table
+ $prevtable =& $this->getTable($table->getPrevious());
+ $nexttable =& $this->getTable($table->getNext());
+ /// Change their previous and next attributes
+ if ($prevtable) {
+ $prevtable->setNext($table->getNext());
+ }
+ if ($nexttable) {
+ $nexttable->setPrevious($table->getPrevious());
+ }
+ /// Delete the table
+ unset($this->tables[$i]);
+ /// Reorder the tables
+ $this->orderTables($this->tables);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one deleted table, so the structure has changed
+ $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+ $this->setChanged(true);
+ }
+ }
+
+ /**
+ * Delete one statement from the Structure
+ */
+ function deleteStatement($statementname) {
+
+ $statement =& $this->getStatement($statementname);
+ if ($statement) {
+ $i = $this->findStatementInArray($statementname);
+ $prevstatement = NULL;
+ $nextstatement = NULL;
+ /// Look for prev and next statement
+ $prevstatement =& $this->getStatement($statement->getPrevious());
+ $nextstatement =& $this->getStatement($statement->getNext());
+ /// Change their previous and next attributes
+ if ($prevstatement) {
+ $prevstatement->setNext($statement->getNext());
+ }
+ if ($nextstatement) {
+ $nextstatement->setPrevious($statement->getPrevious());
+ }
+ /// Delete the statement
+ unset($this->statements[$i]);
+ /// Reorder the statements
+ $this->orderStatements($this->statements);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one deleted statement, so the structure has changed
+ $this->setVersion(userdate(time(), '%Y%m%d', 99, false));
+ $this->setChanged(true);
+ }
+ }
+
+ /**
+ * Set the tables
+ */
+ function setTables(&$tables) {
+ $this->tables = $tables;
+ }
+
+ /**
+ * Set the statements
+ */
+ function setStatements(&$statements) {
+ $this->statements = $statements;
+ }
+
+ /**
+ * Load data from XML to the structure
+ */
+ function arr2XMLDBStructure($xmlarr) {
+
+ $result = true;
+
+ /// Debug the structure
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process structure attributes (path, comment and version)
+ if (isset($xmlarr['XMLDB']['@']['PATH'])) {
+ $this->path = trim($xmlarr['XMLDB']['@']['PATH']);
+ } else {
+ $this->errormsg = 'Missing PATH attribute';
+ $result = false;
+ }
+ if (isset($xmlarr['XMLDB']['@']['VERSION'])) {
+ $this->version = trim($xmlarr['XMLDB']['@']['VERSION']);
+ } else {
+ $this->errormsg = 'Missing VERSION attribute';
+ $result = false;
+ }
+ if (isset($xmlarr['XMLDB']['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['XMLDB']['@']['COMMENT']);
+ } else {
+ $this->errormsg = 'Missing COMMENT attribute';
+ $result = false;
+ }
+
+ /// Iterate over tables
+ if (isset($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'])) {
+ foreach ($xmlarr['XMLDB']['#']['TABLES']['0']['#']['TABLE'] as $xmltable) {
+ if (!$result) { //Skip on error
+ continue;
+ }
+ $name = trim($xmltable['@']['NAME']);
+ $table = new XMLDBTable($name);
+ $table->arr2XMLDBTable($xmltable);
+ $this->tables[] = $table;
+ if (!$table->isLoaded()) {
+ $this->errormsg = 'Problem loading table ' . $name;
+ $result = false;
+ }
+ }
+ } else {
+ $this->errormsg = 'Missing TABLES section';
+ $result = false;
+ }
+
+ /// Perform some general checks over tables
+ if ($result && $this->tables) {
+ /// Check tables names are ok (lowercase, a-z _-)
+ if (!$this->checkNameValues($this->tables)) {
+ $this->errormsg = 'Some TABLES name values are incorrect';
+ $result = false;
+ }
+ /// Check previous & next are ok (duplicates and existing tables)
+ if ($result && !$this->checkPreviousNextValues($this->tables)) {
+ $this->errormsg = 'Some TABLES previous/next values are incorrect';
+ $result = false;
+ }
+ /// Order tables
+ if ($result && !$this->orderTables($this->tables)) {
+ $this->errormsg = 'Error ordering the tables';
+ $result = false;
+ }
+ }
+
+ /// Iterate over statements
+ if (isset($xmlarr['XMLDB']['#']['STATEMENTS']['0']['#']['STATEMENT'])) {
+ foreach ($xmlarr['XMLDB']['#']['STATEMENTS']['0']['#']['STATEMENT'] as $xmlstatement) {
+ if (!$result) { //Skip on error
+ continue;
+ }
+ $name = trim($xmlstatement['@']['NAME']);
+ $statement = new XMLDBStatement($name);
+ $statement->arr2XMLDBStatement($xmlstatement);
+ $this->statements[] = $statement;
+ if (!$statement->isLoaded()) {
+ $this->errormsg = 'Problem loading statement ' . $name;
+ $result = false;
+ }
+ }
+ }
+
+ /// Perform some general checks over statements
+ if ($result && $this->statements) {
+ /// Check statements names are ok (lowercase, a-z _-)
+ if (!$this->checkNameValues($this->statements)) {
+ $this->errormsg = 'Some STATEMENTS name values are incorrect';
+ $result = false;
+ }
+ /// Check previous & next are ok (duplicates and existing statements)
+ if ($result && !$this->checkPreviousNextValues($this->statements)) {
+ $this->errormsg = 'Some STATEMENTS previous/next values are incorrect';
+ $result = false;
+ }
+ /// Order statements
+ if ($result && !$this->orderStatements($this->statements)) {
+ $this->errormsg = 'Error ordering the statements';
+ $result = false;
+ }
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBStructure
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->name . $this->path . $this->comment;
+ if ($this->tables) {
+ foreach ($this->tables as $tbl) {
+ $table =& $this->getTable($tbl->getName());
+ if ($recursive) {
+ $table->calculateHash($recursive);
+ }
+ $key .= $table->getHash();
+ }
+ }
+ if ($this->statements) {
+ foreach ($this->statements as $sta) {
+ $statement =& $this->getStatement($sta->getName());
+ if ($recursive) {
+ $statement->calculateHash($recursive);
+ }
+ $key .= $statement->getHash();
+ }
+ }
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ * This function will output the XML text for one structure
+ */
+ function xmlOutput() {
+ $o = '<?xml version="1.0" encoding="UTF-8" ?>' . "\n";
+ $o.= '<XMLDB PATH="' . $this->path . '"';
+ $o.= ' VERSION="' . $this->version . '"';
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ $o.= '>' . "\n";
+ /// Now the tables
+ if ($this->tables) {
+ $o.= ' <TABLES>' . "\n";
+ foreach ($this->tables as $table) {
+ $o.= $table->xmlOutput();
+ }
+ $o.= ' </TABLES>' . "\n";
+ }
+ /// Now the statements
+ if ($this->statements) {
+ $o.= ' <STATEMENTS>' . "\n";
+ foreach ($this->statements as $statement) {
+ $o.= $statement->xmlOutput();
+ }
+ $o.= ' </STATEMENTS>' . "\n";
+ }
+ $o.= '</XMLDB>';
+
+ return $o;
+ }
+
+ /**
+ * This function returns the number of uses of one table inside
+ * a whole XMLDStructure. Useful to detect if the table must be
+ * locked. Return false if no uses are found.
+ */
+ function getTableUses($tablename) {
+
+ $uses = array();
+
+ /// Check if some foreign key in the whole structure is using it
+ /// (by comparing the reftable with the tablename)
+ $alltables = $this->getTables();
+ if ($alltables) {
+ foreach ($alltables as $table) {
+ $keys = $table->getKeys();
+ if ($keys) {
+ foreach ($keys as $key) {
+ if ($key->getType() == XMLDB_KEY_FOREIGN) {
+ if ($tablename == $key->getRefTable()) {
+ $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Return result
+ if (!empty($uses)) {
+ return $uses;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function returns the number of uses of one field inside
+ * a whole XMLDBStructure. Useful to detect if the field must be
+ * locked. Return false if no uses are found.
+ */
+ function getFieldUses($tablename, $fieldname) {
+
+ $uses = array();
+
+ /// Check if any key in the table is using it
+ $table = $this->getTable($tablename);
+ if ($keys = $table->getKeys()) {
+ foreach ($keys as $key) {
+ if (in_array($fieldname, $key->getFields()) ||
+ in_array($fieldname, $key->getRefFields())) {
+ $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+ }
+ }
+ }
+ /// Check if any index in the table is using it
+ $table = $this->getTable($tablename);
+ if ($indexes = $table->getIndexes()) {
+ foreach ($indexes as $index) {
+ if (in_array($fieldname, $index->getFields())) {
+ $uses[] = 'table ' . $table->getName() . ' index ' . $index->getName();
+ }
+ }
+ }
+ /// Check if some foreign key in the whole structure is using it
+ /// By comparing the reftable and refields with the field)
+ $alltables = $this->getTables();
+ if ($alltables) {
+ foreach ($alltables as $table) {
+ $keys = $table->getKeys();
+ if ($keys) {
+ foreach ($keys as $key) {
+ if ($key->getType() == XMLDB_KEY_FOREIGN) {
+ if ($tablename == $key->getRefTable()) {
+ $reffieds = $key->getRefFields();
+ if (in_array($fieldname, $key->getRefFields())) {
+ $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /// Return result
+ if (!empty($uses)) {
+ return $uses;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function returns the number of uses of one key inside
+ * a whole XMLDBStructure. Useful to detect if the key must be
+ * locked. Return false if no uses are found.
+ */
+ function getKeyUses($tablename, $keyname) {
+
+ $uses = array();
+
+ /// Check if some foreign key in the whole structure is using it
+ /// (by comparing the reftable and reffields with the fields in the key)
+ $mytable = $this->getTable($tablename);
+ $mykey = $mytable->getKey($keyname);
+ $alltables = $this->getTables();
+ if ($alltables && $mykey) {
+ foreach ($alltables as $table) {
+ $allkeys = $table->getKeys();
+ if ($allkeys) {
+ foreach ($allkeys as $key) {
+ if ($key->getType() != XMLDB_KEY_FOREIGN) {
+ continue;
+ }
+ if ($key->getRefTable() == $tablename &&
+ implode(',', $key->getRefFields()) == implode(',', $mykey->getFields())) {
+ $uses[] = 'table ' . $table->getName() . ' key ' . $key->getName();
+ }
+ }
+ }
+ }
+ }
+
+ /// Return result
+ if (!empty($uses)) {
+ return $uses;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function returns the number of uses of one index inside
+ * a whole XMLDBStructure. Useful to detect if the index must be
+ * locked. Return false if no uses are found.
+ */
+ function getIndexUses($tablename, $indexname) {
+
+ $uses = array();
+
+ /// Nothing to check, beause indexes haven't uses! Leave it here
+ /// for future checks...
+
+ /// Return result
+ if (!empty($uses)) {
+ return $uses;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function will return all the errors found in one structure
+ * looking recursively inside each table/statement. Returns
+ * an array of errors or false
+ */
+ function getAllErrors() {
+
+ $errors = array();
+ /// First the structure itself
+ if ($this->getError()) {
+ $errors[] = $this->getError();
+ }
+ /// Delegate to tables
+ if ($tables = $this->getTables()) {
+ foreach ($tables as $table) {
+ if ($tableerrors = $table->getAllErrors()) {
+
+ }
+ }
+ /// Add them to the errors array
+ if ($tableerrors) {
+ $errors = array_merge($errors, $tableerrors);
+ }
+ }
+ /// Delegate to statements
+ if ($statements = $this->getStatements()) {
+ foreach ($statements as $statement) {
+ if ($statement->getError()) {
+ $errors[] = $statement->getError();
+ }
+ }
+ }
+ /// Return decision
+ if (count($errors)) {
+ return $errors;
+ } else {
+ return false;
+ }
+ }
+}
+
+?>
--- /dev/null
+<?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 one XMLDB table
+
+class XMLDBTable extends XMLDBObject {
+
+ var $fields;
+ var $keys;
+ var $indexes;
+
+ /**
+ * Creates one new XMLDBTable
+ */
+ function XMLDBTable($name) {
+ parent::XMLDBObject($name);
+ $this->fields = array();
+ $this->keys = array();
+ $this->indexes = array();
+ }
+
+ /**
+ * Add one field to the table, allowing to specify the desired order
+ * If it's not specified, then the field is added at the end
+ */
+ function addField(&$field, $after=NULL) {
+
+ /// Calculate the previous and next fields
+ $prevfield = NULL;
+ $nextfield = NULL;
+
+ if (!$after) {
+ $allfields =& $this->getFields();
+ if (!empty($allfields)) {
+ end($allfields);
+ $prevfield =& $allfields[key($allfields)];
+ }
+ } else {
+ $prevfield =& $this->getField($after);
+ }
+ if ($prevfield && $prevfield->getNext()) {
+ $nextfield =& $this->getField($prevfield->getNext());
+ }
+
+ /// Set current field previous and next attributes
+ if ($prevfield) {
+ $field->setPrevious($prevfield->getName());
+ $prevfield->setNext($field->getName());
+ }
+ if ($nextfield) {
+ $field->setNext($nextfield->getName());
+ $nextfield->setPrevious($field->getName());
+ }
+ /// Some more attributes
+ $field->setLoaded(true);
+ $field->setChanged(true);
+ /// Add the new field
+ $this->fields[] = $field;
+ /// Reorder the field
+ $this->orderFields($this->fields);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one new field, so the table has changed
+ $this->setChanged(true);
+ }
+
+ /**
+ * Add one key to the table, allowing to specify the desired order
+ * If it's not specified, then the key is added at the end
+ */
+ function addKey(&$key, $after=NULL) {
+
+ /// Calculate the previous and next keys
+ $prevkey = NULL;
+ $nextkey = NULL;
+
+ if (!$after) {
+ $allkeys =& $this->getKeys();
+ if (!empty($allkeys)) {
+ end($allkeys);
+ $prevkey =& $allkeys[key($allkeys)];
+ }
+ } else {
+ $prevkey =& $this->getKey($after);
+ }
+ if ($prevkey && $prevkey->getNext()) {
+ $nextkey =& $this->getKey($prevkey->getNext());
+ }
+
+ /// Set current key previous and next attributes
+ if ($prevkey) {
+ $key->setPrevious($prevkey->getName());
+ $prevkey->setNext($key->getName());
+ }
+ if ($nextkey) {
+ $key->setNext($nextkey->getName());
+ $nextkey->setPrevious($key->getName());
+ }
+ /// Some more attributes
+ $key->setLoaded(true);
+ $key->setChanged(true);
+ /// Add the new key
+ $this->keys[] = $key;
+ /// Reorder the keys
+ $this->orderKeys($this->keys);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one new field, so the table has changed
+ $this->setChanged(true);
+ }
+
+ /**
+ * Add one index to the table, allowing to specify the desired order
+ * If it's not specified, then the index is added at the end
+ */
+ function addIndex(&$index, $after=NULL) {
+
+ /// Calculate the previous and next indexes
+ $previndex = NULL;
+ $nextindex = NULL;
+
+ if (!$after) {
+ $allindexes =& $this->getIndexes();
+ if (!empty($allindexes)) {
+ end($allindexes);
+ $previndex =& $allindexes[key($allindexes)];
+ }
+ } else {
+ $previndex =& $this->getIndex($after);
+ }
+ if ($previndex && $previndex->getNext()) {
+ $nextindex =& $this->getIndex($previndex->getNext());
+ }
+
+ /// Set current index previous and next attributes
+ if ($previndex) {
+ $index->setPrevious($previndex->getName());
+ $previndex->setNext($index->getName());
+ }
+ if ($nextindex) {
+ $index->setNext($nextindex->getName());
+ $nextindex->setPrevious($index->getName());
+ }
+
+ /// Some more attributes
+ $index->setLoaded(true);
+ $index->setChanged(true);
+ /// Add the new index
+ $this->indexes[] = $index;
+ /// Reorder the indexes
+ $this->orderIndexes($this->indexes);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one new index, so the table has changed
+ $this->setChanged(true);
+ }
+
+ /**
+ * This function will return the array of fields in the table
+ */
+ function &getFields() {
+ return $this->fields;
+ }
+
+ /**
+ * This function will return the array of keys in the table
+ */
+ function &getKeys() {
+ return $this->keys;
+ }
+
+ /**
+ * This function will return the array of indexes in the table
+ */
+ function &getIndexes() {
+ return $this->indexes;
+ }
+
+ /**
+ * Returns one XMLDBField
+ */
+ function &getField($fieldname) {
+ $i = $this->findFieldInArray($fieldname);
+ if ($i !== NULL) {
+ return $this->fields[$i];
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the position of one field in the array.
+ */
+ function &findFieldInArray($fieldname) {
+ foreach ($this->fields as $i => $field) {
+ if ($fieldname == $field->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * This function will reorder the array of fields
+ */
+ function orderFields() {
+ $result =& $this->orderElements($this->fields);
+ if ($result) {
+ $this->setFields($result);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns one XMLDBKey
+ */
+ function &getKey($keyname) {
+ $i = $this->findKeyInArray($keyname);
+ if ($i !== NULL) {
+ return $this->keys[$i];
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the position of one key in the array.
+ */
+ function &findKeyInArray($keyname) {
+ foreach ($this->keys as $i => $key) {
+ if ($keyname == $key->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * This function will reorder the array of keys
+ */
+ function orderKeys() {
+ $result =& $this->orderElements($this->keys);
+ if ($result) {
+ $this->setKeys($result);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns one XMLDBIndex
+ */
+ function &getIndex($indexname) {
+ $i = $this->findIndexInArray($indexname);
+ if ($i !== NULL) {
+ return $this->indexes[$i];
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * Returns the position of one index in the array.
+ */
+ function &findIndexInArray($indexname) {
+ foreach ($this->indexes as $i => $index) {
+ if ($indexname == $index->getName()) {
+ return $i;
+ }
+ }
+ $null = NULL;
+ return $null;
+ }
+
+ /**
+ * This function will reorder the array of indexes
+ */
+ function orderIndexes() {
+ $result =& $this->orderElements($this->indexes);
+ if ($result) {
+ $this->setIndexes($result);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function will set the array of fields in the table
+ */
+ function setFields($fields) {
+ $this->fields = $fields;
+ }
+
+ /**
+ * This function will set the array of keys in the table
+ */
+ function setKeys($keys) {
+ $this->keys = $keys;
+ }
+
+ /**
+ * This function will set the array of indexes in the table
+ */
+ function setIndexes($indexes) {
+ $this->indexes = $indexes;
+ }
+
+ /**
+ * Delete one field from the table
+ */
+ function deleteField($fieldname) {
+
+ $field =& $this->getField($fieldname);
+ if ($field) {
+ $i = $this->findFieldInArray($fieldname);
+ $prevfield = NULL;
+ $nextfield = NULL;
+ /// Look for prev and next field
+ $prevfield =& $this->getField($field->getPrevious());
+ $nextfield =& $this->getField($field->getNext());
+ /// Change their previous and next attributes
+ if ($prevfield) {
+ $prevfield->setNext($field->getNext());
+ }
+ if ($nextfield) {
+ $nextfield->setPrevious($field->getPrevious());
+ }
+ /// Delete the field
+ unset($this->fields[$i]);
+ /// Reorder the whole structure
+ $this->orderFields($this->fields);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one deleted field, so the table has changed
+ $this->setChanged(true);
+ }
+ }
+
+ /**
+ * Delete one key from the table
+ */
+ function deleteKey($keyname) {
+
+ $key =& $this->getKey($keyname);
+ if ($key) {
+ $i = $this->findKeyInArray($keyname);
+ $prevkey = NULL;
+ $nextkey = NULL;
+ /// Look for prev and next key
+ $prevkey =& $this->getKey($key->getPrevious());
+ $nextkey =& $this->getKey($key->getNext());
+ /// Change their previous and next attributes
+ if ($prevkey) {
+ $prevkey->setNext($key->getNext());
+ }
+ if ($nextkey) {
+ $nextkey->setPrevious($key->getPrevious());
+ }
+ /// Delete the key
+ unset($this->keys[$i]);
+ /// Reorder the Keys
+ $this->orderKeys($this->keys);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one deleted key, so the table has changed
+ $this->setChanged(true);
+ }
+ }
+
+ /**
+ * Delete one index from the table
+ */
+ function deleteIndex($indexname) {
+
+ $index =& $this->getIndex($indexname);
+ if ($index) {
+ $i = $this->findIndexInArray($indexname);
+ $previndex = NULL;
+ $nextindex = NULL;
+ /// Look for prev and next index
+ $previndex =& $this->getIndex($index->getPrevious());
+ $nextindex =& $this->getIndex($index->getNext());
+ /// Change their previous and next attributes
+ if ($previndex) {
+ $previndex->setNext($index->getNext());
+ }
+ if ($nextindex) {
+ $nextindex->setPrevious($index->getPrevious());
+ }
+ /// Delete the index
+ unset($this->indexes[$i]);
+ /// Reorder the indexes
+ $this->orderIndexes($this->indexes);
+ /// Recalculate the hash
+ $this->calculateHash(true);
+ /// We have one deleted index, so the table has changed
+ $this->setChanged(true);
+ }
+ }
+
+ /**
+ * Load data from XML to the table
+ */
+ function arr2XMLDBTable($xmlarr) {
+
+ $result = true;
+
+ /// Debug the table
+ /// traverse_xmlize($xmlarr); //Debug
+ /// print_object ($GLOBALS['traverse_array']); //Debug
+ /// $GLOBALS['traverse_array']=""; //Debug
+
+ /// Process table attributes (name, comment, previoustable and nexttable)
+ if (isset($xmlarr['@']['NAME'])) {
+ $this->name = trim($xmlarr['@']['NAME']);
+ } else {
+ $this->errormsg = 'Missing NAME attribute';
+ $result = false;
+ }
+ if (isset($xmlarr['@']['COMMENT'])) {
+ $this->comment = trim($xmlarr['@']['COMMENT']);
+ } else {
+ $this->errormsg = 'Missing COMMENT attribute';
+ $result = false;
+ }
+ if (isset($xmlarr['@']['PREVIOUS'])) {
+ $this->previous = trim($xmlarr['@']['PREVIOUS']);
+ }
+ if (isset($xmlarr['@']['NEXT'])) {
+ $this->next = trim($xmlarr['@']['NEXT']);
+ }
+
+ /// Iterate over fields
+ if (isset($xmlarr['#']['FIELDS']['0']['#']['FIELD'])) {
+ foreach ($xmlarr['#']['FIELDS']['0']['#']['FIELD'] as $xmlfield) {
+ if (!$result) { //Skip on error
+ continue;
+ }
+ $name = trim($xmlfield['@']['NAME']);
+ $field = new XMLDBField($name);
+ $field->arr2XMLDBField($xmlfield);
+ $this->fields[] = $field;
+ if (!$field->isLoaded()) {
+ $this->errormsg = 'Problem loading field ' . $name;
+ $result = false;
+ }
+ }
+ } else {
+ $this->errormsg = 'Missing FIELDS section';
+ $result = false;
+ }
+
+ /// Perform some general checks over fields
+ if ($result && $this->fields) {
+ /// Check field names are ok (lowercase, a-z _-)
+ if (!$this->checkNameValues($this->fields)) {
+ $this->errormsg = 'Some FIELDS name values are incorrect';
+ $result = false;
+ }
+ /// Check previous & next are ok (duplicates and existing fields)
+ if ($result && !$this->checkPreviousNextValues($this->fields)) {
+ $this->errormsg = 'Some FIELDS previous/next values are incorrect';
+ $result = false;
+ }
+ /// Order fields
+ if ($result && !$this->orderFields($this->fields)) {
+ $this->errormsg = 'Error ordering the fields';
+ $result = false;
+ }
+ }
+
+ /// Iterate over keys
+ if (isset($xmlarr['#']['KEYS']['0']['#']['KEY'])) {
+ foreach ($xmlarr['#']['KEYS']['0']['#']['KEY'] as $xmlkey) {
+ if (!$result) { //Skip on error
+ continue;
+ }
+ $name = trim($xmlkey['@']['NAME']);
+ $key = new XMLDBKey($name);
+ $key->arr2XMLDBKey($xmlkey);
+ $this->keys[] = $key;
+ if (!$key->isLoaded()) {
+ $this->errormsg = 'Problem loading key ' . $name;
+ $result = false;
+ }
+ }
+ } else {
+ $this->errormsg = 'Missing KEYS section (at least one PK must exist)';
+ $result = false;
+ }
+
+ /// Perform some general checks over keys
+ if ($result && $this->keys) {
+ /// Check keys names are ok (lowercase, a-z _-)
+ if (!$this->checkNameValues($this->keys)) {
+ $this->errormsg = 'Some KEYS name values are incorrect';
+ $result = false;
+ }
+ /// Check previous & next are ok (duplicates and existing keys)
+ if ($result && !$this->checkPreviousNextValues($this->keys)) {
+ $this->errormsg = 'Some KEYS previous/next values are incorrect';
+ $result = false;
+ }
+ /// Order keys
+ if ($result && !$this->orderKeys($this->keys)) {
+ $this->errormsg = 'Error ordering the keys';
+ $result = false;
+ }
+ /// TODO: Only one PK
+ /// TODO: Not keys with repeated fields
+ /// TODO: Check fields and reffieds exist in table
+ }
+
+ /// Iterate over indexes
+ if (isset($xmlarr['#']['INDEXES']['0']['#']['INDEX'])) {
+ foreach ($xmlarr['#']['INDEXES']['0']['#']['INDEX'] as $xmlindex) {
+ if (!$result) { //Skip on error
+ continue;
+ }
+ $name = trim($xmlindex['@']['NAME']);
+ $index = new XMLDBIndex($name);
+ $index->arr2XMLDBIndex($xmlindex);
+ $this->indexes[] = $index;
+ if (!$index->isLoaded()) {
+ $this->errormsg = 'Problem loading index ' . $name;
+ $result = false;
+ }
+ }
+ }
+
+ /// Perform some general checks over indexes
+ if ($result && $this->indexes) {
+ /// Check field names are ok (lowercase, a-z _-)
+ if (!$this->checkNameValues($this->indexes)) {
+ $this->errormsg = 'Some INDEXES name values are incorrect';
+ $result = false;
+ }
+ /// Check previous & next are ok (duplicates and existing INDEXES)
+ if ($result && !$this->checkPreviousNextValues($this->indexes)) {
+ $this->errormsg = 'Some INDEXES previous/next values are incorrect';
+ $result = false;
+ }
+ /// Order indexes
+ if ($result && !$this->orderIndexes($this->indexes)) {
+ $this->errormsg = 'Error ordering the indexes';
+ $result = false;
+ }
+ /// TODO: Not indexes with repeated fields
+ /// TODO: Check fields exist in table
+ }
+
+ /// Set some attributes
+ if ($result) {
+ $this->loaded = true;
+ }
+ $this->calculateHash();
+ return $result;
+ }
+
+ /**
+ * This function calculate and set the hash of one XMLDBTable
+ */
+ function calculateHash($recursive = false) {
+ if (!$this->loaded) {
+ $this->hash = NULL;
+ } else {
+ $key = $this->name . $this->comment;
+ if ($this->fields) {
+ foreach ($this->fields as $fie) {
+ $field =& $this->getField($fie->getName());
+ if ($recursive) {
+ $field->calculateHash($recursive);
+ }
+ $key .= $field->getHash();
+ }
+ }
+ if ($this->keys) {
+ foreach ($this->keys as $ke) {
+ $k =& $this->getKey($ke->getName());
+ if ($recursive) {
+ $k->calculateHash($recursive);
+ }
+ $key .= $k->getHash();
+ }
+ }
+ if ($this->indexes) {
+ foreach ($this->indexes as $in) {
+ $index =& $this->getIndex($in->getName());
+ if ($recursive) {
+ $index->calculateHash($recursive);
+ }
+ $key .= $index->getHash();
+ }
+ }
+ $this->hash = md5($key);
+ }
+ }
+
+ /**
+ * This function will output the XML text for one table
+ */
+ function xmlOutput() {
+ $o = '';
+ $o.= ' <TABLE NAME="' . $this->name . '"';
+ if ($this->comment) {
+ $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"';
+ }
+ if ($this->previous) {
+ $o.= ' PREVIOUS="' . $this->previous . '"';
+ }
+ if ($this->next) {
+ $o.= ' NEXT="' . $this->next . '"';
+ }
+ $o.= '>' . "\n";
+ /// Now the fields
+ if ($this->fields) {
+ $o.= ' <FIELDS>' . "\n";
+ foreach ($this->fields as $field) {
+ $o.= $field->xmlOutput();
+ }
+ $o.= ' </FIELDS>' . "\n";
+ }
+ /// Now the keys
+ if ($this->keys) {
+ $o.= ' <KEYS>' . "\n";
+ foreach ($this->keys as $key) {
+ $o.= $key->xmlOutput();
+ }
+ $o.= ' </KEYS>' . "\n";
+ }
+ /// Now the indexes
+ if ($this->indexes) {
+ $o.= ' <INDEXES>' . "\n";
+ foreach ($this->indexes as $index) {
+ $o.= $index->xmlOutput();
+ }
+ $o.= ' </INDEXES>' . "\n";
+ }
+ $o.= ' </TABLE>' . "\n";
+
+ return $o;
+ }
+
+ /**
+ * This function will return all the errors found in one table
+ * looking recursively inside each field/key/index. Returns
+ * an array of errors or false
+ */
+ function getAllErrors() {
+
+ $errors = array();
+ /// First the table itself
+ if ($this->getError()) {
+ $errors[] = $this->getError();
+ }
+ /// Delegate to fields
+ if ($fields = $this->getFields()) {
+ foreach ($fields as $field) {
+ if ($field->getError()) {
+ $errors[] = $field->getError();
+ }
+ }
+ }
+ /// Delegate to keys
+ if ($keys = $this->getKeys()) {
+ foreach ($keys as $key) {
+ if ($key->getError()) {
+ $errors[] = $key->getError();
+ }
+ }
+ }
+ /// Delegate to indexes
+ if ($indexes = $this->getIndexes()) {
+ foreach ($indexes as $index) {
+ if ($index->getError()) {
+ $errors[] = $index->getError();
+ }
+ }
+ }
+ /// Return decision
+ if (count($errors)) {
+ return $errors;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This function will return the SQL code needed to create the table for the specified DB and
+ * prefix. Just one simple wrapper over generators.
+ */
+ function getCreateTableSQL ($dbtype, $prefix) {
+ $classname = 'XMLDB' . $dbtype;
+ $generator = new $classname();
+ $generator->setPrefix($prefix);
+ return $generator->getCreateTableSQL($this);
+ }
+}
+
+?>
--- /dev/null
+<?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 XMLDBmysql {
+
+ var $quote_string = '`'; // String used to quote names
+
+ var $primary_keys = true; // Does the constructor build primary keys
+ var $unique_keys = false; // Does the constructor build unique keys
+ var $foreign_keys = false; // Does the constructor build foreign keys
+
+ var $primary_index = false;// Does the constructor need to build one index for primary keys
+ var $unique_index = true; // Does the constructor need to build one index for unique keys
+ var $foreign_index = true; // Does the constructor need to build one index for foreign keys
+
+ var $prefix; // Prefix to be used for all the DB objects
+
+
+ /**
+ * Creates one new XMLDBmysql
+ */
+ function XMLDBmysql() {
+ global $CFG;
+ $this->prefix = '';
+ }
+
+ /**
+ * Set the prefix
+ */
+ function setPrefix($prefix) {
+ $this->prefix = $prefix;
+ }
+
+ /**
+ * Given one XMLDB Type, lenght and decimals, returns the DB proper SQL type
+ */
+ function getType ($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
+
+ switch ($xmldb_type) {
+ case XMLDB_TYPE_INTEGER: // From http://mysql.com/doc/refman/5.0/en/numeric-types.html!
+ if (empty($xmldb_length)) {
+ $xmldb_length = 10;
+ }
+ if ($xmldb_length > 9) {
+ $dbtype = 'BIGINT';
+ $xmldb_length = 10;
+ } else if ($xmldb_length > 6) {
+ $dbtype = 'INT';
+ } else if ($xmldb_length > 4) {
+ $dbtype = 'MEDIUMINT';
+ } else if ($xmldb_length > 2) {
+ $dbtype = 'SMALLINT';
+ } else {
+ $dbtype = 'TINTINT';
+ }
+ $dbtype .= '(' . $xmldb_length . ')';
+ break;
+ case XMLDB_TYPE_NUMBER:
+ $dbtype = 'DECIMAL';
+ if (!empty($xmldb_length)) {
+ $dbtype .= '(' . $xmldb_length;
+ if (!empty($xmldb_decimals)) {
+ $dbtype .= ',' . $xmldb_decimals;
+ }
+ $dbtype .= ')';
+ }
+ break;
+ case XMLDB_TYPE_FLOAT:
+ $dbtype = 'FLOAT';
+ if (!empty($xmldb_length)) {
+ $dbtype .= '(' . $xmldb_length;
+ if (!empty($xmldb_decimals)) {
+ $dbtype .= ',' . $xmldb_decimals;
+ }
+ $dbtype .= ')';
+ }
+ break;
+ case XMLDB_TYPE_CHAR:
+ $dbtype = 'VARCHAR';
+ if (!empty($xmldb_length)) {
+ $xmldb_length='255';
+ }
+ $dbtype .= '(' . $xmldb_length . ')';
+ break;
+ case XMLDB_TYPE_TEXT:
+ if (empty($xmldb_length)) {
+ $xmldb_length = 'small';
+ }
+ if ($xmldb_length = 'small') {
+ $dbtype = 'TEXT';
+ } else if ($xmldb_length = 'medium') {
+ $dbtype = 'MEDIUMTEXT';
+ } else {
+ $dbtype = 'BIGTEXT';
+ }
+ break;
+ case XMLDB_TYPE_BINARY:
+ if (empty($xmldb_length)) {
+ $xmldb_length = 'small';
+ }
+ if ($xmldb_length = 'small') {
+ $dbtype = 'BLOB';
+ } else if ($xmldb_length = 'medium') {
+ $dbtype = 'MEDIUMBLOB';
+ } else {
+ $dbtype = 'BIGBLOB';
+ }
+ break;
+ case XMLDB_TYPE_DATETIME:
+ $dbtype = 'DATETIME';
+ }
+ return $dbtype;
+ }
+
+ /**
+ * 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->getCreateFieldSQL($xmldb_field) . ',';
+ }
+ /// Table footer, trim the latest comma
+ $table = trim($table,',');
+ $table .= "\n)";
+ if ($xmldb_table->getComment()) {
+ $table .= " COMMENT='" . $xmldb_table->getComment() . "';\n\n";
+ }
+ return $table;
+ }
+
+ /**
+ * Given one correct XMLDBField, returns the complete SQL line to create it
+ */
+ function getCreateFieldSQL($xmldb_field) {
+
+ /// The name
+ $field = $this->getEncQuoted($xmldb_field->getName());
+ /// The type and length (if the field isn't enum)
+ if (!$xmldb_field->getEnum()) {
+ $field .= ' ' . $this->getType($xmldb_field->getType(), $xmldb_field->getLength(), $xmldb_field->getDecimals());
+ } else {
+ /// If enum, do it with its values
+ $field .= ' enum(' . implode(', ', $xmldb_field->getEnumValues()) . ')';
+ }
+ /// The unsigned
+ if ($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 .= ' auto_increment';
+ }
+ /// 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 ($xmldb_field->getType() == XMLDB_TYPE_CHAR &&
+ $xmldb_field->getNotNull()) {
+ $field .= ' default ' . "''";
+ }
+ }
+ return $field;
+ }
+
+ /**
+ * Given any string, enclose it by the proper quotes
+ */
+ function getEncQuoted($string) {
+ return $this->quote_string . $string . $this->quote_string;
+ }
+
+}
+
+?>
--- /dev/null
+<!ELEMENT FIELD EMPTY >
+<!ATTLIST FIELD DECIMALS NMTOKEN #IMPLIED >
+<!ATTLIST FIELD DEFAULT NMTOKEN #IMPLIED >
+<!ATTLIST FIELD ENUM ( false | true ) #REQUIRED >
+<!ATTLIST FIELD ENUMVALUES CDATA #IMPLIED >
+<!ATTLIST FIELD LENGTH NMTOKEN #REQUIRED >
+<!ATTLIST FIELD NAME NMTOKEN #REQUIRED >
+<!ATTLIST FIELD NEXT NMTOKEN #IMPLIED >
+<!ATTLIST FIELD NOTNULL ( false | true ) #REQUIRED >
+<!ATTLIST FIELD PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST FIELD SEQUENCE ( false | true ) #REQUIRED >
+<!ATTLIST FIELD TYPE ( binary | char | float | int | number | text ) #REQUIRED >
+<!ATTLIST FIELD UNSIGNED ( false | true ) #IMPLIED >
+
+<!ELEMENT FIELDS ( FIELD+ ) >
+
+<!ELEMENT INDEX EMPTY >
+<!ATTLIST INDEX COMMENT CDATA #IMPLIED >
+<!ATTLIST INDEX FIELDS CDATA #REQUIRED >
+<!ATTLIST INDEX NAME NMTOKEN #REQUIRED >
+<!ATTLIST INDEX NEXT NMTOKEN #IMPLIED >
+<!ATTLIST INDEX PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST INDEX UNIQUE ( false | true ) #REQUIRED >
+
+<!ELEMENT INDEXES ( INDEX+ ) >
+
+<!ELEMENT KEY EMPTY >
+<!ATTLIST KEY COMMENT CDATA #IMPLIED >
+<!ATTLIST KEY FIELDS CDATA #REQUIRED >
+<!ATTLIST KEY NAME NMTOKEN #REQUIRED >
+<!ATTLIST KEY NEXT NMTOKEN #IMPLIED >
+<!ATTLIST KEY PREVIOUS NMTOKEN #IMPLIED >
+<!ATTLIST KEY TYPE ( primary | unique | foreign ) #REQUIRED >
+
+<!ELEMENT KEYS ( KEY+ ) >
+
+<!ELEMENT SENTENCE EMPTY >
+<!ATTLIST SENTENCE TEXT CDATA #REQUIRED >
+
+<!ELEMENT SENTENCES ( SENTENCE+ ) >
+
+<!ELEMENT STATEMENT ( SENTENCES ) >
+<!ATTLIST STATEMENT COMMENT CDATA #REQUIRED >
+<!ATTLIST STATEMENT NAME CDATA #REQUIRED >
+<!ATTLIST STATEMENT TABLE NMTOKEN #REQUIRED >
+<!ATTLIST STATEMENT TYPE NMTOKEN #REQUIRED >
+
+<!ELEMENT STATEMENTS ( STATEMENT ) >
+
+<!ELEMENT TABLE ( FIELDS, KEYS, INDEXES? ) >
+<!ATTLIST TABLE COMMENT CDATA #REQUIRED >
+<!ATTLIST TABLE NAME ID #REQUIRED >
+<!ATTLIST TABLE NEXT NMTOKEN #IMPLIED >
+<!ATTLIST TABLE PREVIOUS NMTOKEN #IMPLIED >
+
+<!ELEMENT TABLES ( TABLE+ ) >
+
+<!ELEMENT XMLDB ( TABLES, STATEMENTS ) >
+<!ATTLIST XMLDB COMMENT CDATA #REQUIRED >
+<!ATTLIST XMLDB PATH CDATA #REQUIRED >
+<!ATTLIST XMLDB VERSION NMTOKEN #REQUIRED >
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
+ <xs:element name="FIELD">
+ <xs:complexType>
+ <xs:attribute name="ENUM" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="false" />
+ <xs:enumeration value="true" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="LENGTH" type="xs:NMTOKEN" use="required" />
+ <xs:attribute name="DECIMALS" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="SEQUENCE" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="false" />
+ <xs:enumeration value="true" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="ENUMVALUES" type="xs:string" use="optional" />
+ <xs:attribute name="DEFAULT" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="TYPE" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="binary" />
+ <xs:enumeration value="char" />
+ <xs:enumeration value="float" />
+ <xs:enumeration value="int" />
+ <xs:enumeration value="number" />
+ <xs:enumeration value="text" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="UNSIGNED" use="optional">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="false" />
+ <xs:enumeration value="true" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="NOTNULL" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="false" />
+ <xs:enumeration value="true" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="NAME" type="xs:NMTOKEN" use="required" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="FIELDS">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="FIELD" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="INDEX">
+ <xs:complexType>
+ <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="COMMENT" type="xs:string" use="optional" />
+ <xs:attribute name="UNIQUE" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="false" />
+ <xs:enumeration value="true" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="NAME" type="xs:NMTOKEN" use="required" />
+ <xs:attribute name="FIELDS" type="xs:string" use="required" />
+ <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="INDEXES">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="INDEX" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="KEY">
+ <xs:complexType>
+ <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="COMMENT" type="xs:string" use="optional" />
+ <xs:attribute name="NAME" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="name" />
+ <xs:enumeration value="plugin_name" />
+ <xs:enumeration value="primary" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="FIELDS" type="xs:string" use="required" />
+ <xs:attribute name="TYPE" use="required">
+ <xs:simpleType>
+ <xs:restriction base="xs:NMTOKEN">
+ <xs:enumeration value="primary" />
+ <xs:enumeration value="unique" />
+ <xs:enumeration value="foreign" />
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="KEYS">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="KEY" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="SENTENCE">
+ <xs:complexType>
+ <xs:attribute name="TEXT" type="xs:string" use="required" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="SENTENCES">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="SENTENCE" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="STATEMENT">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="SENTENCES" />
+ </xs:sequence>
+ <xs:attribute name="COMMENT" type="xs:string" use="required" />
+ <xs:attribute name="TABLE" type="xs:NMTOKEN" use="required" />
+ <xs:attribute name="NAME" type="xs:string" use="required" />
+ <xs:attribute name="TYPE" type="xs:NMTOKEN" use="required" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="STATEMENTS">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="STATEMENT" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="TABLE">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="FIELDS" />
+ <xs:element ref="KEYS" />
+ <xs:element ref="INDEXES" minOccurs="0" />
+ </xs:sequence>
+ <xs:attribute name="PREVIOUS" type="xs:NMTOKEN" use="optional" />
+ <xs:attribute name="COMMENT" type="xs:string" use="required" />
+ <xs:attribute name="NAME" type="xs:ID" use="required" />
+ <xs:attribute name="NEXT" type="xs:NMTOKEN" use="optional" />
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="TABLES">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="TABLE" maxOccurs="unbounded" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+
+ <xs:element name="XMLDB">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element ref="TABLES" />
+ <xs:element ref="STATEMENTS" />
+ </xs:sequence>
+ <xs:attribute name="PATH" type="xs:string" use="required" />
+ <xs:attribute name="COMMENT" type="xs:string" use="required" />
+ <xs:attribute name="VERSION" type="xs:NMTOKEN" use="required" />
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
\ No newline at end of file