Skip to content
Snippets Groups Projects
adodb-xmlschema03.inc.php 60.7 KiB
Newer Older
Prasad's avatar
Prasad committed
<?php
// Copyright (c) 2004-2005 ars Cognita Inc., all rights reserved
/* ******************************************************************************
Satish's avatar
Satish committed
    Released under both BSD license and Lesser GPL library license.
 	Whenever there is any discrepancy between the two licenses,
 	the BSD license will take precedence.
Prasad's avatar
Prasad committed
*******************************************************************************/
/**
 * xmlschema is a class that allows the user to quickly and easily
 * build a database on any ADOdb-supported platform using a simple
 * XML schema.
 *
 * Last Editor: $Author: jlim $
 * @author Richard Tango-Lowy & Dan Cech
 * @version $Revision: 1.62 $
 *
 * @package axmls
 * @tutorial getting_started.pkg
 */
Satish's avatar
Satish committed

function _file_get_contents($file)
Prasad's avatar
Prasad committed
{
 	if (function_exists('file_get_contents')) return file_get_contents($file);
Prasad's avatar
Prasad committed
	$f = fopen($file,'r');
	if (!$f) return '';
	$t = '';
Prasad's avatar
Prasad committed
	while ($s = fread($f,100000)) $t .= $s;
	fclose($f);
	return $t;
}


/**
* Debug on or off
*/
if( !defined( 'XMLS_DEBUG' ) ) {
	define( 'XMLS_DEBUG', FALSE );
}

/**
* Default prefix key
*/
if( !defined( 'XMLS_PREFIX' ) ) {
	define( 'XMLS_PREFIX', '%%P' );
}

/**
* Maximum length allowed for object prefix
*/
if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
	define( 'XMLS_PREFIX_MAXLEN', 10 );
}

/**
* Execute SQL inline as it is generated
*/
if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
	define( 'XMLS_EXECUTE_INLINE', FALSE );
}

/**
* Continue SQL Execution if an error occurs?
*/
if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
	define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
}

/**
* Current Schema Version
*/
if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
	define( 'XMLS_SCHEMA_VERSION', '0.3' );
}

/**
* Default Schema Version.  Used for Schemas without an explicit version set.
*/
if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
	define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
}

/**
* How to handle data rows that already exist in a database during and upgrade.
* Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing
* rows) and IGNORE (ignores existing rows).
*/
if( !defined( 'XMLS_MODE_INSERT' ) ) {
	define( 'XMLS_MODE_INSERT', 0 );
}
if( !defined( 'XMLS_MODE_UPDATE' ) ) {
	define( 'XMLS_MODE_UPDATE', 1 );
}
if( !defined( 'XMLS_MODE_IGNORE' ) ) {
	define( 'XMLS_MODE_IGNORE', 2 );
}
if( !defined( 'XMLS_EXISTING_DATA' ) ) {
	define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );
}

/**
* Default Schema Version.  Used for Schemas without an explicit version set.
*/
if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
	define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
}

/**
* Include the main ADODB library
*/
if( !defined( '_ADODB_LAYER' ) ) {
	require( 'adodb.inc.php' );
	require( 'adodb-datadict.inc.php' );
}

/**
* Abstract DB Object. This class provides basic methods for database objects, such
* as tables and indexes.
*
* @package axmls
* @access private
*/
class dbObject {
Prasad's avatar
Prasad committed
	/**
	* var object Parent
	*/
	var $parent;
Prasad's avatar
Prasad committed
	/**
	* var string current element
	*/
	var $currentElement;
Prasad's avatar
Prasad committed
	/**
	* NOP
	*/
Satish's avatar
Satish committed
	function __construct( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process start elements
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process CDATA elements
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
Prasad's avatar
Prasad committed
	}
Satish's avatar
Satish committed

	function create(&$xmls) {
Prasad's avatar
Prasad committed
		return array();
	}
Prasad's avatar
Prasad committed
	/**
	* Destroys the object
	*/
	function destroy() {
	}
Prasad's avatar
Prasad committed
	/**
	* Checks whether the specified RDBMS is supported by the current
	* database object or its ranking ancestor.
	*
	* @param string $platform RDBMS platform name (from ADODB platform list).
	* @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
	*/
	function supportedPlatform( $platform = NULL ) {
		return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
	}
Prasad's avatar
Prasad committed
	/**
	* Returns the prefix set by the ranking ancestor of the database object.
	*
	* @param string $name Prefix string.
	* @return string Prefix.
	*/
	function prefix( $name = '' ) {
		return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
	}
Prasad's avatar
Prasad committed
	/**
	* Extracts a field ID from the specified field.
	*
	* @param string $field Field.
	* @return string Field ID.
	*/
	function FieldID( $field ) {
		return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
	}
}

/**
* Creates a table object in ADOdb's datadict format
*
* This class stores information about a database table. As charactaristics
* of the table are loaded from the external source, methods and properties
* of this class are used to build up the table description in ADOdb's
* datadict format.
*
* @package axmls
* @access private
*/
class dbTable extends dbObject {
Prasad's avatar
Prasad committed
	/**
	* @var string Table name
	*/
	var $name;
Prasad's avatar
Prasad committed
	/**
	* @var array Field specifier: Meta-information about each field
	*/
	var $fields = array();
Prasad's avatar
Prasad committed
	/**
	* @var array List of table indexes.
	*/
	var $indexes = array();
Prasad's avatar
Prasad committed
	/**
	* @var array Table options: Table-level options
	*/
	var $opts = array();
Prasad's avatar
Prasad committed
	/**
	* @var string Field index: Keeps track of which field is currently being processed
	*/
	var $current_field;
Prasad's avatar
Prasad committed
	/**
	* @var boolean Mark table for destruction
	* @access private
	*/
	var $drop_table;
Prasad's avatar
Prasad committed
	/**
	* @var boolean Mark field for destruction (not yet implemented)
	* @access private
	*/
	var $drop_field = array();
Prasad's avatar
Prasad committed
	/**
	* @var array Platform-specific options
	* @access private
	*/
	var $currentPlatform = true;
Prasad's avatar
Prasad committed
	/**
	* Iniitializes a new table object.
	*
	* @param string $prefix DB Object prefix
	* @param array $attributes Array of table attributes.
	*/
Satish's avatar
Satish committed
	function __construct( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
Prasad's avatar
Prasad committed
		$this->name = $this->prefix($attributes['NAME']);
	}
Prasad's avatar
Prasad committed
	/**
Satish's avatar
Satish committed
	* XML Callback to process start elements. Elements currently
	* processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT.
Prasad's avatar
Prasad committed
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
Prasad's avatar
Prasad committed
		switch( $this->currentElement ) {
			case 'INDEX':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
Satish's avatar
Satish committed
					$index = $this->addIndex( $attributes );
					xml_set_object( $parser,  $index );
Prasad's avatar
Prasad committed
				}
				break;
			case 'DATA':
				if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
Satish's avatar
Satish committed
					$data = $this->addData( $attributes );
					xml_set_object( $parser, $data );
Prasad's avatar
Prasad committed
				}
				break;
			case 'DROP':
				$this->drop();
				break;
			case 'FIELD':
				// Add a field
				$fieldName = $attributes['NAME'];
				$fieldType = $attributes['TYPE'];
				$fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
				$fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
Prasad's avatar
Prasad committed
				$this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
				break;
			case 'KEY':
			case 'NOTNULL':
			case 'AUTOINCREMENT':
			case 'DEFDATE':
			case 'DEFTIMESTAMP':
			case 'UNSIGNED':
				// Add a field option
				$this->addFieldOpt( $this->current_field, $this->currentElement );
				break;
			case 'DEFAULT':
				// Add a field option to the table object
Prasad's avatar
Prasad committed
				// Work around ADOdb datadict issue that misinterprets empty strings.
				if( $attributes['VALUE'] == '' ) {
					$attributes['VALUE'] = " '' ";
				}
Prasad's avatar
Prasad committed
				$this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
				break;
			case 'OPT':
			case 'CONSTRAINT':
				// Accept platform-specific options
				$this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process CDATA elements
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Table/field constraint
			case 'CONSTRAINT':
				if( isset( $this->current_field ) ) {
					$this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
				} else {
					$this->addTableOpt( $cdata );
				}
				break;
			// Table/field option
			case 'OPT':
				if( isset( $this->current_field ) ) {
					$this->addFieldOpt( $this->current_field, $cdata );
				} else {
				$this->addTableOpt( $cdata );
				}
				break;
			default:
Prasad's avatar
Prasad committed
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
Prasad's avatar
Prasad committed
		switch( strtoupper( $tag ) ) {
			case 'TABLE':
				$this->parent->addSQL( $this->create( $this->parent ) );
				xml_set_object( $parser, $this->parent );
				$this->destroy();
				break;
			case 'FIELD':
				unset($this->current_field);
				break;
			case 'OPT':
			case 'CONSTRAINT':
				$this->currentPlatform = true;
				break;
			default:

		}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds an index to a table object
	*
	* @param array $attributes Index attributes
	* @return object dbIndex object
	*/
Satish's avatar
Satish committed
	function addIndex( $attributes ) {
Prasad's avatar
Prasad committed
		$name = strtoupper( $attributes['NAME'] );
Satish's avatar
Satish committed
		$this->indexes[$name] = new dbIndex( $this, $attributes );
Prasad's avatar
Prasad committed
		return $this->indexes[$name];
	}
Prasad's avatar
Prasad committed
	/**
	* Adds data to a table object
	*
	* @param array $attributes Data attributes
	* @return object dbData object
	*/
Satish's avatar
Satish committed
	function addData( $attributes ) {
Prasad's avatar
Prasad committed
		if( !isset( $this->data ) ) {
Satish's avatar
Satish committed
			$this->data = new dbData( $this, $attributes );
Prasad's avatar
Prasad committed
		}
		return $this->data;
	}
Prasad's avatar
Prasad committed
	/**
	* Adds a field to a table object
	*
Satish's avatar
Satish committed
	* $name is the name of the table to which the field should be added.
Prasad's avatar
Prasad committed
	* $type is an ADODB datadict field type. The following field types
	* are supported as of ADODB 3.40:
	* 	- C:  varchar
	*	- X:  CLOB (character large object) or largest varchar size
	*	   if CLOB is not supported
	*	- C2: Multibyte varchar
	*	- X2: Multibyte CLOB
	*	- B:  BLOB (binary large object)
	*	- D:  Date (some databases do not support this, and we return a datetime type)
	*	- T:  Datetime or Timestamp
	*	- L:  Integer field suitable for storing booleans (0 or 1)
	*	- I:  Integer (mapped to I4)
	*	- I1: 1-byte integer
	*	- I2: 2-byte integer
	*	- I4: 4-byte integer
	*	- I8: 8-byte integer
	*	- F:  Floating point number
	*	- N:  Numeric or decimal number
	*
	* @param string $name Name of the table to which the field will be added.
	* @param string $type	ADODB datadict field type.
	* @param string $size	Field size
	* @param array $opts	Field options array
	* @return array Field specifier array
	*/
	function addField( $name, $type, $size = NULL, $opts = NULL ) {
		$field_id = $this->FieldID( $name );
Prasad's avatar
Prasad committed
		// Set the field index so we know where we are
		$this->current_field = $field_id;
Prasad's avatar
Prasad committed
		// Set the field name (required)
		$this->fields[$field_id]['NAME'] = $name;
Prasad's avatar
Prasad committed
		// Set the field type (required)
		$this->fields[$field_id]['TYPE'] = $type;
Prasad's avatar
Prasad committed
		// Set the field size (optional)
		if( isset( $size ) ) {
			$this->fields[$field_id]['SIZE'] = $size;
		}
Prasad's avatar
Prasad committed
		// Set the field options
		if( isset( $opts ) ) {
			$this->fields[$field_id]['OPTS'] = array($opts);
		} else {
			$this->fields[$field_id]['OPTS'] = array();
		}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds a field option to the current field specifier
	*
Satish's avatar
Satish committed
	* This method adds a field option allowed by the ADOdb datadict
Prasad's avatar
Prasad committed
	* and appends it to the given field.
	*
	* @param string $field	Field name
	* @param string $opt ADOdb field option
	* @param mixed $value Field option value
	* @return array Field specifier array
	*/
	function addFieldOpt( $field, $opt, $value = NULL ) {
		if( $this->currentPlatform ) {
		if( !isset( $value ) ) {
			$this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
		// Add the option and value
		} else {
			$this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
		}
	}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds an option to the table
	*
	* This method takes a comma-separated list of table-level options
	* and appends them to the table object.
	*
	* @param string $opt Table option
	* @return array Options
	*/
	function addTableOpt( $opt ) {
Satish's avatar
Satish committed
		if(isset($this->currentPlatform)) {
			$this->opts[$this->parent->db->databaseType] = $opt;
Prasad's avatar
Prasad committed
		}
		return $this->opts;
	}
Prasad's avatar
Prasad committed
	/**
	* Generates the SQL that will create the table in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing table creation SQL
	*/
	function create( &$xmls ) {
		$sql = array();
Prasad's avatar
Prasad committed
		// drop any existing indexes
		if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
			foreach( $legacy_indexes as $index => $index_details ) {
				$sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
			}
		}
Prasad's avatar
Prasad committed
		// remove fields to be dropped from table object
		foreach( $this->drop_field as $field ) {
			unset( $this->fields[$field] );
		}
Prasad's avatar
Prasad committed
		// if table exists
		if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
			// drop table
			if( $this->drop_table ) {
				$sql[] = $xmls->dict->DropTableSQL( $this->name );
Prasad's avatar
Prasad committed
				return $sql;
			}
Prasad's avatar
Prasad committed
			// drop any existing fields not in schema
			foreach( $legacy_fields as $field_id => $field ) {
				if( !isset( $this->fields[$field_id] ) ) {
					$sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
				}
			}
		// if table doesn't exist
		} else {
			if( $this->drop_table ) {
				return $sql;
			}
Prasad's avatar
Prasad committed
			$legacy_fields = array();
		}
Prasad's avatar
Prasad committed
		// Loop through the field specifier array, building the associative array for the field options
		$fldarray = array();
Prasad's avatar
Prasad committed
		foreach( $this->fields as $field_id => $finfo ) {
			// Set an empty size if it isn't supplied
			if( !isset( $finfo['SIZE'] ) ) {
				$finfo['SIZE'] = '';
			}
Prasad's avatar
Prasad committed
			// Initialize the field array with the type and size
			$fldarray[$field_id] = array(
				'NAME' => $finfo['NAME'],
				'TYPE' => $finfo['TYPE'],
				'SIZE' => $finfo['SIZE']
			);
Satish's avatar
Satish committed

			// Loop through the options array and add the field options.
Prasad's avatar
Prasad committed
			if( isset( $finfo['OPTS'] ) ) {
				foreach( $finfo['OPTS'] as $opt ) {
					// Option has an argument.
					if( is_array( $opt ) ) {
						$key = key( $opt );
						$value = $opt[key( $opt )];
						@$fldarray[$field_id][$key] .= $value;
					// Option doesn't have arguments
					} else {
						$fldarray[$field_id][$opt] = $opt;
					}
				}
			}
		}
Prasad's avatar
Prasad committed
		if( empty( $legacy_fields ) ) {
			// Create the new table
			$sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
			logMsg( end( $sql ), 'Generated CreateTableSQL' );
		} else {
			// Upgrade an existing table
			logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
			switch( $xmls->upgrade ) {
				// Use ChangeTableSQL
				case 'ALTER':
					logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
					$sql[] = $xmls->dict->ChangeTableSQL( $this->name, $fldarray, $this->opts );
					break;
				case 'REPLACE':
					logMsg( 'Doing upgrade REPLACE (testing)' );
					$sql[] = $xmls->dict->DropTableSQL( $this->name );
					$sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
					break;
				// ignore table
				default:
					return array();
			}
		}
Prasad's avatar
Prasad committed
		foreach( $this->indexes as $index ) {
			$sql[] = $index->create( $xmls );
		}
Prasad's avatar
Prasad committed
		if( isset( $this->data ) ) {
			$sql[] = $this->data->create( $xmls );
		}
Prasad's avatar
Prasad committed
		return $sql;
	}
Prasad's avatar
Prasad committed
	/**
	* Marks a field or table for destruction
	*/
	function drop() {
		if( isset( $this->current_field ) ) {
			// Drop the current field
			logMsg( "Dropping field '{$this->current_field}' from table '{$this->name}'" );
			// $this->drop_field[$this->current_field] = $xmls->dict->DropColumnSQL( $this->name, $this->current_field );
			$this->drop_field[$this->current_field] = $this->current_field;
		} else {
			// Drop the current table
			logMsg( "Dropping table '{$this->name}'" );
			// $this->drop_table = $xmls->dict->DropTableSQL( $this->name );
			$this->drop_table = TRUE;
		}
	}
}

/**
* Creates an index object in ADOdb's datadict format
*
* This class stores information about a database index. As charactaristics
* of the index are loaded from the external source, methods and properties
* of this class are used to build up the index description in ADOdb's
* datadict format.
*
* @package axmls
* @access private
*/
class dbIndex extends dbObject {
Prasad's avatar
Prasad committed
	/**
	* @var string	Index name
	*/
	var $name;
Prasad's avatar
Prasad committed
	/**
	* @var array	Index options: Index-level options
	*/
	var $opts = array();
Prasad's avatar
Prasad committed
	/**
	* @var array	Indexed fields: Table columns included in this index
	*/
	var $columns = array();
Prasad's avatar
Prasad committed
	/**
	* @var boolean Mark index for destruction
	* @access private
	*/
	var $drop = FALSE;
Prasad's avatar
Prasad committed
	/**
	* Initializes the new dbIndex object.
	*
	* @param object $parent Parent object
	* @param array $attributes Attributes
	*
	* @internal
	*/
Satish's avatar
Satish committed
	function __construct( &$parent, $attributes = NULL ) {
		$this->parent = $parent;

Prasad's avatar
Prasad committed
		$this->name = $this->prefix ($attributes['NAME']);
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process start elements
	*
Satish's avatar
Satish committed
	* Processes XML opening tags.
	* Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH.
Prasad's avatar
Prasad committed
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
Prasad's avatar
Prasad committed
		switch( $this->currentElement ) {
			case 'DROP':
				$this->drop();
				break;
			case 'CLUSTERED':
			case 'BITMAP':
			case 'UNIQUE':
			case 'FULLTEXT':
			case 'HASH':
				// Add index Option
				$this->addIndexOpt( $this->currentElement );
				break;
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process CDATA elements
	*
	* Processes XML cdata.
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Index field name
			case 'COL':
				$this->addField( $cdata );
				break;
			default:
Prasad's avatar
Prasad committed
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
Prasad's avatar
Prasad committed
		switch( strtoupper( $tag ) ) {
			case 'INDEX':
				xml_set_object( $parser, $this->parent );
				break;
		}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds a field to the index
	*
	* @param string $name Field name
	* @return string Field list
	*/
	function addField( $name ) {
		$this->columns[$this->FieldID( $name )] = $name;
Prasad's avatar
Prasad committed
		// Return the field list
		return $this->columns;
	}
Prasad's avatar
Prasad committed
	/**
	* Adds options to the index
	*
	* @param string $opt Comma-separated list of index options.
	* @return string Option list
	*/
	function addIndexOpt( $opt ) {
		$this->opts[] = $opt;
Prasad's avatar
Prasad committed
		// Return the options list
		return $this->opts;
	}
Prasad's avatar
Prasad committed
	/**
	* Generates the SQL that will create the index in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing index creation SQL
	*/
	function create( &$xmls ) {
		if( $this->drop ) {
			return NULL;
		}
Prasad's avatar
Prasad committed
		// eliminate any columns that aren't in the table
		foreach( $this->columns as $id => $col ) {
			if( !isset( $this->parent->fields[$id] ) ) {
				unset( $this->columns[$id] );
			}
		}
Prasad's avatar
Prasad committed
		return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts );
	}
Prasad's avatar
Prasad committed
	/**
	* Marks an index for destruction
	*/
	function drop() {
		$this->drop = TRUE;
	}
}

/**
* Creates a data object in ADOdb's datadict format
*
* This class stores information about table data, and is called
* when we need to load field data into a table.
*
* @package axmls
* @access private
*/
class dbData extends dbObject {
Prasad's avatar
Prasad committed
	var $data = array();
Prasad's avatar
Prasad committed
	var $row;
Prasad's avatar
Prasad committed
	/**
	* Initializes the new dbData object.
	*
	* @param object $parent Parent object
	* @param array $attributes Attributes
	*
	* @internal
	*/
Satish's avatar
Satish committed
	function __construct( &$parent, $attributes = NULL ) {
		$this->parent = $parent;
Prasad's avatar
Prasad committed
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process start elements
	*
Satish's avatar
Satish committed
	* Processes XML opening tags.
	* Elements currently processed are: ROW and F (field).
Prasad's avatar
Prasad committed
	*
	* @access private
	*/
	function _tag_open( &$parser, $tag, $attributes ) {
		$this->currentElement = strtoupper( $tag );
Prasad's avatar
Prasad committed
		switch( $this->currentElement ) {
			case 'ROW':
				$this->row = count( $this->data );
				$this->data[$this->row] = array();
				break;
			case 'F':
				$this->addField($attributes);
			default:
				// print_r( array( $tag, $attributes ) );
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process CDATA elements
	*
	* Processes XML cdata.
	*
	* @access private
	*/
	function _tag_cdata( &$parser, $cdata ) {
		switch( $this->currentElement ) {
			// Index field name
			case 'F':
				$this->addData( $cdata );
				break;
			default:
Prasad's avatar
Prasad committed
		}
	}
Prasad's avatar
Prasad committed
	/**
	* XML Callback to process end elements
	*
	* @access private
	*/
	function _tag_close( &$parser, $tag ) {
		$this->currentElement = '';
Prasad's avatar
Prasad committed
		switch( strtoupper( $tag ) ) {
			case 'DATA':
				xml_set_object( $parser, $this->parent );
				break;
		}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds a field to the insert
	*
	* @param string $name Field name
	* @return string Field list
	*/
	function addField( $attributes ) {
		// check we're in a valid row
		if( !isset( $this->row ) || !isset( $this->data[$this->row] ) ) {
			return;
		}
Prasad's avatar
Prasad committed
		// Set the field index so we know where we are
		if( isset( $attributes['NAME'] ) ) {
			$this->current_field = $this->FieldID( $attributes['NAME'] );
		} else {
			$this->current_field = count( $this->data[$this->row] );
		}
Prasad's avatar
Prasad committed
		// initialise data
		if( !isset( $this->data[$this->row][$this->current_field] ) ) {
			$this->data[$this->row][$this->current_field] = '';
		}
	}
Prasad's avatar
Prasad committed
	/**
	* Adds options to the index
	*
	* @param string $opt Comma-separated list of index options.
	* @return string Option list
	*/
	function addData( $cdata ) {
		// check we're in a valid field
		if ( isset( $this->data[$this->row][$this->current_field] ) ) {
			// add data to field
			$this->data[$this->row][$this->current_field] .= $cdata;
		}
	}
Prasad's avatar
Prasad committed
	/**
	* Generates the SQL that will add/update the data in the database
	*
	* @param object $xmls adoSchema object
	* @return array Array containing index creation SQL
	*/
	function create( &$xmls ) {
		$table = $xmls->dict->TableName($this->parent->name);
		$table_field_count = count($this->parent->fields);
Satish's avatar
Satish committed
		$tables = $xmls->db->MetaTables();
Prasad's avatar
Prasad committed
		$sql = array();
Prasad's avatar
Prasad committed
		$ukeys = $xmls->db->MetaPrimaryKeys( $table );
		if( !empty( $this->parent->indexes ) and !empty( $ukeys ) ) {
			foreach( $this->parent->indexes as $indexObj ) {
				if( !in_array( $indexObj->name, $ukeys ) ) $ukeys[] = $indexObj->name;
			}
		}
Prasad's avatar
Prasad committed
		// eliminate any columns that aren't in the table
		foreach( $this->data as $row ) {
			$table_fields = $this->parent->fields;
			$fields = array();
			$rawfields = array(); // Need to keep some of the unprocessed data on hand.
Prasad's avatar
Prasad committed
			foreach( $row as $field_id => $field_data ) {
				if( !array_key_exists( $field_id, $table_fields ) ) {
					if( is_numeric( $field_id ) ) {
						$field_id = reset( array_keys( $table_fields ) );
					} else {
						continue;
					}
				}
Prasad's avatar
Prasad committed
				$name = $table_fields[$field_id]['NAME'];
Prasad's avatar
Prasad committed
				switch( $table_fields[$field_id]['TYPE'] ) {
					case 'I':
					case 'I1':
					case 'I2':
					case 'I4':
					case 'I8':
						$fields[$name] = intval($field_data);
						break;
					case 'C':
					case 'C2':
					case 'X':
					case 'X2':
					default:
						$fields[$name] = $xmls->db->qstr( $field_data );
						$rawfields[$name] = $field_data;
				}
Prasad's avatar
Prasad committed
				unset($table_fields[$field_id]);
Prasad's avatar
Prasad committed
			}
Prasad's avatar
Prasad committed
			// check that at least 1 column is specified
			if( empty( $fields ) ) {
				continue;
			}
Prasad's avatar
Prasad committed
			// check that no required columns are missing
			if( count( $fields ) < $table_field_count ) {
				foreach( $table_fields as $field ) {
					if( isset( $field['OPTS'] ) and ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) {
							continue(2);
						}
				}
			}