root/releases/elgg0.8rc2/lib/adodb/adodb-xmlschema03.inc.php

Revision 758, 61.2 kB (checked in by sven, 2 years ago)

set svn property eol-style native on some files without it

  • Property svn:eol-style set to native
Line 
1 <?php
2 // Copyright (c) 2004-2005 ars Cognita Inc., all rights reserved
3 /* ******************************************************************************
4     Released under both BSD license and Lesser GPL library license.
5      Whenever there is any discrepancy between the two licenses,
6      the BSD license will take precedence.
7 *******************************************************************************/
8 /**
9  * xmlschema is a class that allows the user to quickly and easily
10  * build a database on any ADOdb-supported platform using a simple
11  * XML schema.
12  *
13  * Last Editor: $Author: jlim $
14  * @author Richard Tango-Lowy & Dan Cech
15  * @version $Revision: 1.62 $
16  *
17  * @package axmls
18  * @tutorial getting_started.pkg
19  */
20  
21 function _file_get_contents($file)
22 {
23      if (function_exists('file_get_contents')) return file_get_contents($file);
24     
25     $f = fopen($file,'r');
26     if (!$f) return '';
27     $t = '';
28     
29     while ($s = fread($f,100000)) $t .= $s;
30     fclose($f);
31     return $t;
32 }
33
34
35 /**
36 * Debug on or off
37 */
38 if( !defined( 'XMLS_DEBUG' ) ) {
39     define( 'XMLS_DEBUG', FALSE );
40 }
41
42 /**
43 * Default prefix key
44 */
45 if( !defined( 'XMLS_PREFIX' ) ) {
46     define( 'XMLS_PREFIX', '%%P' );
47 }
48
49 /**
50 * Maximum length allowed for object prefix
51 */
52 if( !defined( 'XMLS_PREFIX_MAXLEN' ) ) {
53     define( 'XMLS_PREFIX_MAXLEN', 10 );
54 }
55
56 /**
57 * Execute SQL inline as it is generated
58 */
59 if( !defined( 'XMLS_EXECUTE_INLINE' ) ) {
60     define( 'XMLS_EXECUTE_INLINE', FALSE );
61 }
62
63 /**
64 * Continue SQL Execution if an error occurs?
65 */
66 if( !defined( 'XMLS_CONTINUE_ON_ERROR' ) ) {
67     define( 'XMLS_CONTINUE_ON_ERROR', FALSE );
68 }
69
70 /**
71 * Current Schema Version
72 */
73 if( !defined( 'XMLS_SCHEMA_VERSION' ) ) {
74     define( 'XMLS_SCHEMA_VERSION', '0.3' );
75 }
76
77 /**
78 * Default Schema Version.  Used for Schemas without an explicit version set.
79 */
80 if( !defined( 'XMLS_DEFAULT_SCHEMA_VERSION' ) ) {
81     define( 'XMLS_DEFAULT_SCHEMA_VERSION', '0.1' );
82 }
83
84 /**
85 * How to handle data rows that already exist in a database during and upgrade.
86 * Options are INSERT (attempts to insert duplicate rows), UPDATE (updates existing
87 * rows) and IGNORE (ignores existing rows).
88 */
89 if( !defined( 'XMLS_MODE_INSERT' ) ) {
90     define( 'XMLS_MODE_INSERT', 0 );
91 }
92 if( !defined( 'XMLS_MODE_UPDATE' ) ) {
93     define( 'XMLS_MODE_UPDATE', 1 );
94 }
95 if( !defined( 'XMLS_MODE_IGNORE' ) ) {
96     define( 'XMLS_MODE_IGNORE', 2 );
97 }
98 if( !defined( 'XMLS_EXISTING_DATA' ) ) {
99     define( 'XMLS_EXISTING_DATA', XMLS_MODE_INSERT );
100 }
101
102 /**
103 * Default Schema Version.  Used for Schemas without an explicit version set.
104 */
105 if( !defined( 'XMLS_DEFAULT_UPGRADE_METHOD' ) ) {
106     define( 'XMLS_DEFAULT_UPGRADE_METHOD', 'ALTER' );
107 }
108
109 /**
110 * Include the main ADODB library
111 */
112 if( !defined( '_ADODB_LAYER' ) ) {
113     require( 'adodb.inc.php' );
114     require( 'adodb-datadict.inc.php' );
115 }
116
117 /**
118 * Abstract DB Object. This class provides basic methods for database objects, such
119 * as tables and indexes.
120 *
121 * @package axmls
122 * @access private
123 */
124 class dbObject {
125     
126     /**
127     * var object Parent
128     */
129     var $parent;
130     
131     /**
132     * var string current element
133     */
134     var $currentElement;
135     
136     /**
137     * NOP
138     */
139     function dbObject( &$parent, $attributes = NULL ) {
140         $this->parent =& $parent;
141     }
142     
143     /**
144     * XML Callback to process start elements
145     *
146     * @access private
147     */
148     function _tag_open( &$parser, $tag, $attributes ) {
149         
150     }
151     
152     /**
153     * XML Callback to process CDATA elements
154     *
155     * @access private
156     */
157     function _tag_cdata( &$parser, $cdata ) {
158         
159     }
160     
161     /**
162     * XML Callback to process end elements
163     *
164     * @access private
165     */
166     function _tag_close( &$parser, $tag ) {
167         
168     }
169     
170     function create() {
171         return array();
172     }
173     
174     /**
175     * Destroys the object
176     */
177     function destroy() {
178         unset( $this );
179     }
180     
181     /**
182     * Checks whether the specified RDBMS is supported by the current
183     * database object or its ranking ancestor.
184     *
185     * @param string $platform RDBMS platform name (from ADODB platform list).
186     * @return boolean TRUE if RDBMS is supported; otherwise returns FALSE.
187     */
188     function supportedPlatform( $platform = NULL ) {
189         return is_object( $this->parent ) ? $this->parent->supportedPlatform( $platform ) : TRUE;
190     }
191     
192     /**
193     * Returns the prefix set by the ranking ancestor of the database object.
194     *
195     * @param string $name Prefix string.
196     * @return string Prefix.
197     */
198     function prefix( $name = '' ) {
199         return is_object( $this->parent ) ? $this->parent->prefix( $name ) : $name;
200     }
201     
202     /**
203     * Extracts a field ID from the specified field.
204     *
205     * @param string $field Field.
206     * @return string Field ID.
207     */
208     function FieldID( $field ) {
209         return strtoupper( preg_replace( '/^`(.+)`$/', '$1', $field ) );
210     }
211 }
212
213 /**
214 * Creates a table object in ADOdb's datadict format
215 *
216 * This class stores information about a database table. As charactaristics
217 * of the table are loaded from the external source, methods and properties
218 * of this class are used to build up the table description in ADOdb's
219 * datadict format.
220 *
221 * @package axmls
222 * @access private
223 */
224 class dbTable extends dbObject {
225     
226     /**
227     * @var string Table name
228     */
229     var $name;
230     
231     /**
232     * @var array Field specifier: Meta-information about each field
233     */
234     var $fields = array();
235     
236     /**
237     * @var array List of table indexes.
238     */
239     var $indexes = array();
240     
241     /**
242     * @var array Table options: Table-level options
243     */
244     var $opts = array();
245     
246     /**
247     * @var string Field index: Keeps track of which field is currently being processed
248     */
249     var $current_field;
250     
251     /**
252     * @var boolean Mark table for destruction
253     * @access private
254     */
255     var $drop_table;
256     
257     /**
258     * @var boolean Mark field for destruction (not yet implemented)
259     * @access private
260     */
261     var $drop_field = array();
262     
263     /**
264     * @var array Platform-specific options
265     * @access private
266     */
267     var $currentPlatform = true;
268     
269     
270     /**
271     * Iniitializes a new table object.
272     *
273     * @param string $prefix DB Object prefix
274     * @param array $attributes Array of table attributes.
275     */
276     function dbTable( &$parent, $attributes = NULL ) {
277         $this->parent =& $parent;
278         $this->name = $this->prefix($attributes['NAME']);
279     }
280     
281     /**
282     * XML Callback to process start elements. Elements currently
283     * processed are: INDEX, DROP, FIELD, KEY, NOTNULL, AUTOINCREMENT & DEFAULT.
284     *
285     * @access private
286     */
287     function _tag_open( &$parser, $tag, $attributes ) {
288         $this->currentElement = strtoupper( $tag );
289         
290         switch( $this->currentElement ) {
291             case 'INDEX':
292                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
293                     xml_set_object( $parser, $this->addIndex( $attributes ) );
294                 }
295                 break;
296             case 'DATA':
297                 if( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) ) {
298                     xml_set_object( $parser, $this->addData( $attributes ) );
299                 }
300                 break;
301             case 'DROP':
302                 $this->drop();
303                 break;
304             case 'FIELD':
305                 // Add a field
306                 $fieldName = $attributes['NAME'];
307                 $fieldType = $attributes['TYPE'];
308                 $fieldSize = isset( $attributes['SIZE'] ) ? $attributes['SIZE'] : NULL;
309                 $fieldOpts = !empty( $attributes['OPTS'] ) ? $attributes['OPTS'] : NULL;
310                 
311                 $this->addField( $fieldName, $fieldType, $fieldSize, $fieldOpts );
312                 break;
313             case 'KEY':
314             case 'NOTNULL':
315             case 'AUTOINCREMENT':
316             case 'DEFDATE':
317             case 'DEFTIMESTAMP':
318             case 'UNSIGNED':
319                 // Add a field option
320                 $this->addFieldOpt( $this->current_field, $this->currentElement );
321                 break;
322             case 'DEFAULT':
323                 // Add a field option to the table object
324                 
325                 // Work around ADOdb datadict issue that misinterprets empty strings.
326                 if( $attributes['VALUE'] == '' ) {
327                     $attributes['VALUE'] = " '' ";
328                 }
329                 
330                 $this->addFieldOpt( $this->current_field, $this->currentElement, $attributes['VALUE'] );
331                 break;
332             case 'OPT':
333             case 'CONSTRAINT':
334                 // Accept platform-specific options
335                 $this->currentPlatform = ( !isset( $attributes['PLATFORM'] ) OR $this->supportedPlatform( $attributes['PLATFORM'] ) );
336                 break;
337             default:
338                 // print_r( array( $tag, $attributes ) );
339         }
340     }
341     
342     /**
343     * XML Callback to process CDATA elements
344     *
345     * @access private
346     */
347     function _tag_cdata( &$parser, $cdata ) {
348         switch( $this->currentElement ) {
349             // Table/field constraint
350             case 'CONSTRAINT':
351                 if( isset( $this->current_field ) ) {
352                     $this->addFieldOpt( $this->current_field, $this->currentElement, $cdata );
353                 } else {
354                     $this->addTableOpt( $cdata );
355                 }
356                 break;
357             // Table/field option
358             case 'OPT':
359                 if( isset( $this->current_field ) ) {
360                     $this->addFieldOpt( $this->current_field, $cdata );
361                 } else {
362                 $this->addTableOpt( $cdata );
363                 }
364                 break;
365             default:
366                 
367         }
368     }
369     
370     /**
371     * XML Callback to process end elements
372     *
373     * @access private
374     */
375     function _tag_close( &$parser, $tag ) {
376         $this->currentElement = '';
377         
378         switch( strtoupper( $tag ) ) {
379             case 'TABLE':
380                 $this->parent->addSQL( $this->create( $this->parent ) );
381                 xml_set_object( $parser, $this->parent );
382                 $this->destroy();
383                 break;
384             case 'FIELD':
385                 unset($this->current_field);
386                 break;
387             case 'OPT':
388             case 'CONSTRAINT':
389                 $this->currentPlatform = true;
390                 break;
391             default:
392
393         }
394     }
395     
396     /**
397     * Adds an index to a table object
398     *
399     * @param array $attributes Index attributes
400     * @return object dbIndex object
401     */
402     function &addIndex( $attributes ) {
403         $name = strtoupper( $attributes['NAME'] );
404         $this->indexes[$name] =& new dbIndex( $this, $attributes );
405         return $this->indexes[$name];
406     }
407     
408     /**
409     * Adds data to a table object
410     *
411     * @param array $attributes Data attributes
412     * @return object dbData object
413     */
414     function &addData( $attributes ) {
415         if( !isset( $this->data ) ) {
416             $this->data =& new dbData( $this, $attributes );
417         }
418         return $this->data;
419     }
420     
421     /**
422     * Adds a field to a table object
423     *
424     * $name is the name of the table to which the field should be added.
425     * $type is an ADODB datadict field type. The following field types
426     * are supported as of ADODB 3.40:
427     *     - C:  varchar
428     *    - X:  CLOB (character large object) or largest varchar size
429     *       if CLOB is not supported
430     *    - C2: Multibyte varchar
431     *    - X2: Multibyte CLOB
432     *    - B:  BLOB (binary large object)
433     *    - D:  Date (some databases do not support this, and we return a datetime type)
434     *    - T:  Datetime or Timestamp
435     *    - L:  Integer field suitable for storing booleans (0 or 1)
436     *    - I:  Integer (mapped to I4)
437     *    - I1: 1-byte integer
438     *    - I2: 2-byte integer
439     *    - I4: 4-byte integer
440     *    - I8: 8-byte integer
441     *    - F:  Floating point number
442     *    - N:  Numeric or decimal number
443     *
444     * @param string $name Name of the table to which the field will be added.
445     * @param string $type    ADODB datadict field type.
446     * @param string $size    Field size
447     * @param array $opts    Field options array
448     * @return array Field specifier array
449     */
450     function addField( $name, $type, $size = NULL, $opts = NULL ) {
451         $field_id = $this->FieldID( $name );
452         
453         // Set the field index so we know where we are
454         $this->current_field = $field_id;
455         
456         // Set the field name (required)
457         $this->fields[$field_id]['NAME'] = $name;
458         
459         // Set the field type (required)
460         $this->fields[$field_id]['TYPE'] = $type;
461         
462         // Set the field size (optional)
463         if( isset( $size ) ) {
464             $this->fields[$field_id]['SIZE'] = $size;
465         }
466         
467         // Set the field options
468         if( isset( $opts ) ) {
469             $this->fields[$field_id]['OPTS'] = array($opts);
470         } else {
471             $this->fields[$field_id]['OPTS'] = array();
472         }
473     }
474     
475     /**
476     * Adds a field option to the current field specifier
477     *
478     * This method adds a field option allowed by the ADOdb datadict
479     * and appends it to the given field.
480     *
481     * @param string $field    Field name
482     * @param string $opt ADOdb field option
483     * @param mixed $value Field option value
484     * @return array Field specifier array
485     */
486     function addFieldOpt( $field, $opt, $value = NULL ) {
487         if( $this->currentPlatform ) {
488         if( !isset( $value ) ) {
489             $this->fields[$this->FieldID( $field )]['OPTS'][] = $opt;
490         // Add the option and value
491         } else {
492             $this->fields[$this->FieldID( $field )]['OPTS'][] = array( $opt => $value );
493         }
494     }
495     }
496     
497     /**
498     * Adds an option to the table
499     *
500     * This method takes a comma-separated list of table-level options
501     * and appends them to the table object.
502     *
503     * @param string $opt Table option
504     * @return array Options
505     */
506     function addTableOpt( $opt ) {
507         if( $this->currentPlatform ) {
508         $this->opts[] = $opt;
509         }
510         return $this->opts;
511     }
512     
513     /**
514     * Generates the SQL that will create the table in the database
515     *
516     * @param object $xmls adoSchema object
517     * @return array Array containing table creation SQL
518     */
519     function create( &$xmls ) {
520         $sql = array();
521         
522         // drop any existing indexes
523         if( is_array( $legacy_indexes = $xmls->dict->MetaIndexes( $this->name ) ) ) {
524             foreach( $legacy_indexes as $index => $index_details ) {
525                 $sql[] = $xmls->dict->DropIndexSQL( $index, $this->name );
526             }
527         }
528         
529         // remove fields to be dropped from table object
530         foreach( $this->drop_field as $field ) {
531             unset( $this->fields[$field] );
532         }
533         
534         // if table exists
535         if( is_array( $legacy_fields = $xmls->dict->MetaColumns( $this->name ) ) ) {
536             // drop table
537             if( $this->drop_table ) {
538                 $sql[] = $xmls->dict->DropTableSQL( $this->name );
539                 
540                 return $sql;
541             }
542             
543             // drop any existing fields not in schema
544             foreach( $legacy_fields as $field_id => $field ) {
545                 if( !isset( $this->fields[$field_id] ) ) {
546                     $sql[] = $xmls->dict->DropColumnSQL( $this->name, $field->name );
547                 }
548             }
549         // if table doesn't exist
550         } else {
551             if( $this->drop_table ) {
552                 return $sql;
553             }
554             
555             $legacy_fields = array();
556         }
557         
558         // Loop through the field specifier array, building the associative array for the field options
559         $fldarray = array();
560         
561         foreach( $this->fields as $field_id => $finfo ) {
562             // Set an empty size if it isn't supplied
563             if( !isset( $finfo['SIZE'] ) ) {
564                 $finfo['SIZE'] = '';
565             }
566             
567             // Initialize the field array with the type and size
568             $fldarray[$field_id] = array(
569                 'NAME' => $finfo['NAME'],
570                 'TYPE' => $finfo['TYPE'],
571                 'SIZE' => $finfo['SIZE']
572             );
573             
574             // Loop through the options array and add the field options.
575             if( isset( $finfo['OPTS'] ) ) {
576                 foreach( $finfo['OPTS'] as $opt ) {
577                     // Option has an argument.
578                     if( is_array( $opt ) ) {
579                         $key = key( $opt );
580                         $value = $opt[key( $opt )];
581                         @$fldarray[$field_id][$key] .= $value;
582                     // Option doesn't have arguments
583                     } else {
584                         $fldarray[$field_id][$opt] = $opt;
585                     }
586                 }
587             }
588         }
589         
590         if( empty( $legacy_fields ) ) {
591             // Create the new table
592             $sql[] = $xmls->dict->CreateTableSQL( $this->name, $fldarray, $this->opts );
593             logMsg( end( $sql ), 'Generated CreateTableSQL' );
594         } else {
595             // Upgrade an existing table
596             logMsg( "Upgrading {$this->name} using '{$xmls->upgrade}'" );
597             switch( $xmls->upgrade ) {
598                 // Use ChangeTableSQL
599                 case 'ALTER':
600                     logMsg( 'Generated ChangeTableSQL (ALTERing table)' );
601                     $sql[] = $xmls->dict->