From 8165877a1729d798b9fbc1288890bbe4ae3caffb Mon Sep 17 00:00:00 2001 From: stronk7 Date: Tue, 15 Aug 2006 09:14:31 +0000 Subject: [PATCH] Initial commit on HEAD of all the XMLDB stuff. --- lib/xmldb/classes/XMLDBConstants.php | 69 ++ lib/xmldb/classes/XMLDBField.class.php | 733 ++++++++++++++++++ lib/xmldb/classes/XMLDBFile.class.php | 112 +++ lib/xmldb/classes/XMLDBIndex.class.php | 221 ++++++ lib/xmldb/classes/XMLDBKey.class.php | 381 +++++++++ lib/xmldb/classes/XMLDBObject.class.php | 387 +++++++++ lib/xmldb/classes/XMLDBStatement.class.php | 365 +++++++++ lib/xmldb/classes/XMLDBStructure.class.php | 697 +++++++++++++++++ lib/xmldb/classes/XMLDBTable.class.php | 729 +++++++++++++++++ .../classes/generators/mysql/mysql.class.php | 228 ++++++ lib/xmldb/xmldb.dtd | 62 ++ lib/xmldb/xmldb.xsd | 197 +++++ 12 files changed, 4181 insertions(+) create mode 100644 lib/xmldb/classes/XMLDBConstants.php create mode 100644 lib/xmldb/classes/XMLDBField.class.php create mode 100644 lib/xmldb/classes/XMLDBFile.class.php create mode 100644 lib/xmldb/classes/XMLDBIndex.class.php create mode 100644 lib/xmldb/classes/XMLDBKey.class.php create mode 100644 lib/xmldb/classes/XMLDBObject.class.php create mode 100644 lib/xmldb/classes/XMLDBStatement.class.php create mode 100644 lib/xmldb/classes/XMLDBStructure.class.php create mode 100644 lib/xmldb/classes/XMLDBTable.class.php create mode 100644 lib/xmldb/classes/generators/mysql/mysql.class.php create mode 100644 lib/xmldb/xmldb.dtd create mode 100644 lib/xmldb/xmldb.xsd diff --git a/lib/xmldb/classes/XMLDBConstants.php b/lib/xmldb/classes/XMLDBConstants.php new file mode 100644 index 0000000000..89bb098c70 --- /dev/null +++ b/lib/xmldb/classes/XMLDBConstants.php @@ -0,0 +1,69 @@ + diff --git a/lib/xmldb/classes/XMLDBField.class.php b/lib/xmldb/classes/XMLDBField.class.php new file mode 100644 index 0000000000..1f484d70a9 --- /dev/null +++ b/lib/xmldb/classes/XMLDBField.class.php @@ -0,0 +1,733 @@ +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.= ' 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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBFile.class.php b/lib/xmldb/classes/XMLDBFile.class.php new file mode 100644 index 0000000000..24e4010470 --- /dev/null +++ b/lib/xmldb/classes/XMLDBFile.class.php @@ -0,0 +1,112 @@ +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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBIndex.class.php b/lib/xmldb/classes/XMLDBIndex.class.php new file mode 100644 index 0000000000..2fa8947e1f --- /dev/null +++ b/lib/xmldb/classes/XMLDBIndex.class.php @@ -0,0 +1,221 @@ +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.= ' 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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBKey.class.php b/lib/xmldb/classes/XMLDBKey.class.php new file mode 100644 index 0000000000..61f74d6309 --- /dev/null +++ b/lib/xmldb/classes/XMLDBKey.class.php @@ -0,0 +1,381 @@ +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.= ' 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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBObject.class.php b/lib/xmldb/classes/XMLDBObject.class.php new file mode 100644 index 0000000000..efd6f1f694 --- /dev/null +++ b/lib/xmldb/classes/XMLDBObject.class.php @@ -0,0 +1,387 @@ +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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBStatement.class.php b/lib/xmldb/classes/XMLDBStatement.class.php new file mode 100644 index 0000000000..7592803a79 --- /dev/null +++ b/lib/xmldb/classes/XMLDBStatement.class.php @@ -0,0 +1,365 @@ +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.= ' 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.= ' ' . "\n"; + foreach ($this->sentences as $sentence) { + $o.= ' ' . "\n"; + } + $o.= ' ' . "\n"; + $o.= ' ' . "\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; + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBStructure.class.php b/lib/xmldb/classes/XMLDBStructure.class.php new file mode 100644 index 0000000000..73eea8cc0f --- /dev/null +++ b/lib/xmldb/classes/XMLDBStructure.class.php @@ -0,0 +1,697 @@ +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 = '' . "\n"; + $o.= 'version . '"'; + if ($this->comment) { + $o.= ' COMMENT="' . htmlspecialchars($this->comment) . '"'; + } + $o.= '>' . "\n"; + /// Now the tables + if ($this->tables) { + $o.= ' ' . "\n"; + foreach ($this->tables as $table) { + $o.= $table->xmlOutput(); + } + $o.= ' ' . "\n"; + } + /// Now the statements + if ($this->statements) { + $o.= ' ' . "\n"; + foreach ($this->statements as $statement) { + $o.= $statement->xmlOutput(); + } + $o.= ' ' . "\n"; + } + $o.= ''; + + 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; + } + } +} + +?> diff --git a/lib/xmldb/classes/XMLDBTable.class.php b/lib/xmldb/classes/XMLDBTable.class.php new file mode 100644 index 0000000000..fd917bab21 --- /dev/null +++ b/lib/xmldb/classes/XMLDBTable.class.php @@ -0,0 +1,729 @@ +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.= ' 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.= ' ' . "\n"; + foreach ($this->fields as $field) { + $o.= $field->xmlOutput(); + } + $o.= ' ' . "\n"; + } + /// Now the keys + if ($this->keys) { + $o.= ' ' . "\n"; + foreach ($this->keys as $key) { + $o.= $key->xmlOutput(); + } + $o.= ' ' . "\n"; + } + /// Now the indexes + if ($this->indexes) { + $o.= ' ' . "\n"; + foreach ($this->indexes as $index) { + $o.= $index->xmlOutput(); + } + $o.= ' ' . "\n"; + } + $o.= '
' . "\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); + } +} + +?> diff --git a/lib/xmldb/classes/generators/mysql/mysql.class.php b/lib/xmldb/classes/generators/mysql/mysql.class.php new file mode 100644 index 0000000000..fe1074f014 --- /dev/null +++ b/lib/xmldb/classes/generators/mysql/mysql.class.php @@ -0,0 +1,228 @@ +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; + } + +} + +?> diff --git a/lib/xmldb/xmldb.dtd b/lib/xmldb/xmldb.dtd new file mode 100644 index 0000000000..2a5907ebe8 --- /dev/null +++ b/lib/xmldb/xmldb.dtd @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/xmldb/xmldb.xsd b/lib/xmldb/xmldb.xsd new file mode 100644 index 0000000000..ace9053522 --- /dev/null +++ b/lib/xmldb/xmldb.xsd @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.39.5