s = array(); } // Loop through the field specifier array, building the associative array for the field options $fldarray = array(); foreach( $this->fields as $field_id => $finfo ) { // Set an empty size if it isn't supplied if( !isset( $finfo['SIZE'] ) ) { $finfo['SIZE'] = ''; } // Initialize the field array with the type and size $fldarray[$field_id] = array( 'NAME' => $finfo['NAME'], 'TYPE' => $finfo['TYPE'], 'SIZE' => $finfo['SIZE'] ); // Loop through the options array and add the field options. 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; } } } } 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(); } } foreach( $this->indexes as $index ) { $sql[] = $index->create( $xmls ); } if( isset( $this->data ) ) { $sql[] = $this->data->create( $xmls ); } return $sql; } /** * 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 { /** * @var string Index name */ var $name; /** * @var array Index options: Index-level options */ var $opts = array(); /** * @var array Indexed fields: Table columns included in this index */ var $columns = array(); /** * @var boolean Mark index for destruction * @access private */ var $drop = FALSE; /** * Initializes the new dbIndex object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbIndex( &$parent, $attributes = NULL ) { $this->parent =& $parent; $this->name = $this->prefix ($attributes['NAME']); } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); 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 ) ); } } /** * 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: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'INDEX': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the index * * @param string $name Field name * @return string Field list */ function addField( $name ) { $this->columns[$this->FieldID( $name )] = $name; // Return the field list return $this->columns; } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addIndexOpt( $opt ) { $this->opts[] = $opt; // Return the options list return $this->opts; } /** * 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; } // 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] ); } } return $xmls->dict->CreateIndexSQL( $this->name, $this->parent->name, $this->columns, $this->opts ); } /** * 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. * * @package axmls * @access private */ class dbData extends dbObject { var $data = array(); var $row; /** * Initializes the new dbIndex object. * * @param object $parent Parent object * @param array $attributes Attributes * * @internal */ function dbData( &$parent, $attributes = NULL ) { $this->parent =& $parent; } /** * XML Callback to process start elements * * Processes XML opening tags. * Elements currently processed are: DROP, CLUSTERED, BITMAP, UNIQUE, FULLTEXT & HASH. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); 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 ) ); } } /** * 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: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'DATA': xml_set_object( $parser, $this->parent ); break; } } /** * Adds a field to the index * * @param string $name Field name * @return string Field list */ function addField( $attributes ) { if( isset( $attributes['NAME'] ) ) { $name = $attributes['NAME']; } else { $name = count($this->data[$this->row]); } // Set the field index so we know where we are $this->current_field = $this->FieldID( $name ); } /** * Adds options to the index * * @param string $opt Comma-separated list of index options. * @return string Option list */ function addData( $cdata ) { if( !isset( $this->data[$this->row] ) ) { $this->data[$this->row] = array(); } if( !isset( $this->data[$this->row][$this->current_field] ) ) { $this->data[$this->row][$this->current_field] = ''; } $this->data[$this->row][$this->current_field] .= $cdata; } /** * 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 ) { $table = $xmls->dict->TableName($this->parent->name); $table_field_count = count($this->parent->fields); $sql = array(); // eliminate any columns that aren't in the table foreach( $this->data as $row ) { $table_fields = $this->parent->fields; $fields = array(); 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; } } $name = $table_fields[$field_id]['NAME']; switch( $table_fields[$field_id]['TYPE'] ) { case 'C': case 'C2': case 'X': case 'X2': $fields[$name] = $xmls->db->qstr( $field_data ); break; case 'I': case 'I1': case 'I2': case 'I4': case 'I8': $fields[$name] = intval($field_data); break; default: $fields[$name] = $field_data; } unset($table_fields[$field_id]); } // check that at least 1 column is specified if( empty( $fields ) ) { continue; } // check that no required columns are missing if( count( $fields ) < $table_field_count ) { foreach( $table_fields as $field ) { if (isset( $field['OPTS'] )) if( ( in_array( 'NOTNULL', $field['OPTS'] ) || in_array( 'KEY', $field['OPTS'] ) ) && !in_array( 'AUTOINCREMENT', $field['OPTS'] ) ) { continue(2); } } } $sql[] = 'INSERT INTO '. $table .' ('. implode( ',', array_keys( $fields ) ) .') VALUES ('. implode( ',', $fields ) .')'; } return $sql; } } /** * Creates the SQL to execute a list of provided SQL queries * * @package axmls * @access private */ class dbQuerySet extends dbObject { /** * @var array List of SQL queries */ var $queries = array(); /** * @var string String used to build of a query line by line */ var $query; /** * @var string Query prefix key */ var $prefixKey = ''; /** * @var boolean Auto prefix enable (TRUE) */ var $prefixMethod = 'AUTO'; /** * Initializes the query set. * * @param object $parent Parent object * @param array $attributes Attributes */ function dbQuerySet( &$parent, $attributes = NULL ) { $this->parent =& $parent; // Overrides the manual prefix key if( isset( $attributes['KEY'] ) ) { $this->prefixKey = $attributes['KEY']; } $prefixMethod = isset( $attributes['PREFIXMETHOD'] ) ? strtoupper( trim( $attributes['PREFIXMETHOD'] ) ) : ''; // Enables or disables automatic prefix prepending switch( $prefixMethod ) { case 'AUTO': $this->prefixMethod = 'AUTO'; break; case 'MANUAL': $this->prefixMethod = 'MANUAL'; break; case 'NONE': $this->prefixMethod = 'NONE'; break; } } /** * XML Callback to process start elements. Elements currently * processed are: QUERY. * * @access private */ function _tag_open( &$parser, $tag, $attributes ) { $this->currentElement = strtoupper( $tag ); switch( $this->currentElement ) { case 'QUERY': // Create a new query in a SQL queryset. // Ignore this query set if a platform is specified and it's different than the // current connection platform. if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) { $this->newQuery(); } else { $this->discardQuery(); } break; default: // print_r( array( $tag, $attributes ) ); } } /** * XML Callback to process CDATA elements */ function _tag_cdata( &$parser, $cdata ) { switch( $this->currentElement ) { // Line of queryset SQL data case 'QUERY': $this->buildQuery( $cdata ); break; default: } } /** * XML Callback to process end elements * * @access private */ function _tag_close( &$parser, $tag ) { $this->currentElement = ''; switch( strtoupper( $tag ) ) { case 'QUERY': // Add the finished query to the open query set. $this->addQuery(); break; case 'SQL': $this->parent->addSQL( $this->create( $this->parent ) ); xml_set_object( $parser, $this->parent ); $this->destroy(); break; default: } } /** * Re-initializes the query. * * @return boolean TRUE */ function newQuery() { $this->query = ''; return TRUE; } /** * Discards the existing query. * * @return boolean TRUE */ function discardQuery() { unset( $this->query ); return TRUE; } /** * Appends a line to a query that is being built line by line * * @param string $data Line of SQL data or NULL to initialize a new query * @return string SQL query string. */ function buildQuery( $sql = NULL ) { if( !isset( $this->query ) OR empty( $sql ) ) { return FALSE; } $this->query .= $sql; return $this->query; } /** * Adds a completed query to the query list * * @return string SQL of added query */ function addQuery() { if( !isset( $this->query ) ) { return FALSE; } $this->queries[] = $return = trim($this->query); unset( $this->query ); return $return; } /** * Creates and returns the current query set * * @param object $xmls adoSchema object * @return array Query set */ function create( &$xmls ) { foreach( $this->queries as $id => $query ) { switch( $this->prefixMethod ) { case 'AUTO': // Enable auto prefix replacement // Process object prefix. // Evaluate SQL statements to prepend prefix to objects $query = $this->prefixQuery( '/^\s*((?is)INSERT\s+(INTO\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)UPDATE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); $query = $this->prefixQuery( '/^\s*((?is)DELETE\s+(FROM\s+)?)((\w+\s*,?\s*)+)(\s.*$)/', $query, $xmls->objectPrefix ); // SELECT statements aren't working yet #$data = preg_replace( '/(?ias)(^\s*SELECT\s+.*\s+FROM)\s+(\W\s*,?\s*)+((?i)\s+WHERE.*$)/', "\1 $prefix\2 \3", $data ); case 'MANUAL': // If prefixKey is set