13 require_once
'DAL/Parsers/DALQueryParser.inc';
14 require_once
'DAL/DALBaker.inc';
33 private static $_requiresSizeAttr = array(
47 private static $_columnDataTypes = array(
61 'TIME WITH TIME ZONE',
62 'TIME WITHOUT TIME ZONE',
63 'TIMESTAMP WITH TIME ZONE',
64 'TIMESTAMP WITHOUT TIME ZONE',
74 private static $_udts = array();
84 private function __construct()
98 public static function parse(DomElement $schema)
100 self::validate($schema);
103 $query[
'CREATE'] = array();
104 $tables = $schema->getElementsByTagName(
'table');
105 self::$_udts = self::getSchemaUdts($schema);
107 foreach ($tables as $table) {
109 $current[
'table'] = $table->getAttribute(
'name');
110 $current[
'COLUMNS'] = self::getTableColumns($table);
111 $current[
'INDEXES'] = self::getTableIndexes($table);
112 $current[
'CONSTRAINTS'] = self::getTableConstraints($table);
113 $current[
'SEQUENCES'] = self::getTableSequences($table);
115 $query[
'CREATE'][] = $current;
135 'IMPORTED' => array()
138 $udtsTag = $schema->getElementsByTagName(
'udts')->item(0);
139 if ($udtsTag !== NULL) {
141 $ludts = $udtsTag->getElementsByTagName(
'udt');
142 foreach ($ludts as $udt) {
144 $current[
'type'] = $udt->getAttribute(
'type');
145 $current[
'size'] = $udt->getAttribute(
'size');
146 $current[
'scale'] = $udt->getAttribute(
'scale');
148 $schemaUdts[
'LOCAL'][$udt->nodeValue] = $current;
152 $ludts = $udtsTag->getElementsByTagName(
'udt-import');
153 foreach ($ludts as $udt) {
155 $current[
'system'] = $udt->getAttribute(
'system');
157 $schemaUdts[
'IMPORTED'][$udt->nodeValue] = $current;
176 $squencesTag = $table->getElementsByTagName(
'sequences')->item(0);
177 $sequences = array();
179 if ($squencesTag !== NULL) {
180 $seqs = $squencesTag->getElementsByTagName(
'sequence');
181 foreach ($seqs as $seq) {
182 $sequences[] = $seq->getAttribute(
'name');
201 $keys = self::_getPrimaryKeyConstraints($parent);
202 if (empty($keys) === FALSE) {
203 $constraints[
'PRIMARY-KEYS'] = $keys;
206 $keys = self::_getForeignKeyConstraints($parent);
207 if (empty($keys) === FALSE) {
208 $constraints[
'FOREIGN-KEYS'] = $keys;
211 $keys = self::_getUniqueConstraints($parent);
212 if (empty($keys) === FALSE) {
213 $constraints[
'UNIQUES'] = $keys;
231 $constraints = array();
232 $constraintsTag = $table->getElementsByTagName(
'constraints')->item(0);
234 if ($constraintsTag === NULL) {
238 $constraints = self::getConstraintsFromParent($constraintsTag);
253 private static function _getPrimaryKeyConstraints(DomElement $parent)
256 $pks = $parent->getElementsByTagName(
'primary-key');
258 $primaryKeys = array();
259 foreach ($pks as $pk) {
261 $current[
'name'] = $pk->getAttribute(
'name');
262 $current[
'COLUMNS'] = array();
264 $columns = $pk->getElementsByTagName(
'column');
265 foreach ($columns as $column) {
266 $current[
'COLUMNS'][] = $column->nodeValue;
269 $primaryKeys[] = $current;
285 private static function _getForeignKeyConstraints(DomElement $parent)
288 $fks = $parent->getElementsByTagName(
'foreign-key');
290 $foreignKeys = array();
291 foreach ($fks as $fk) {
293 $current[
'name'] = $fk->getAttribute(
'name');
294 $current[
'table'] = $fk->getAttribute(
'foreign-table');
295 $onDelete = $fk->getAttribute(
'on-delete');
296 if ($onDelete ===
'') {
297 $onDelete =
'NO ACTION';
300 $current[
'on-delete'] = $onDelete;
301 $current[
'COLUMNS'] = array();
303 $columns = $fk->getElementsByTagName(
'column');
304 foreach ($columns as $column) {
305 $col[
'name'] = $column->nodeValue;
306 $col[
'references'] = $column->getAttribute(
'references');
307 $current[
'COLUMNS'][] = $col;
310 $foreignKeys[] = $current;
326 private static function _getUniqueConstraints(DomElement $parent)
329 $uns = $parent->getElementsByTagName(
'unique');
332 foreach ($uns as $uq) {
334 $current[
'name'] = $uq->getAttribute(
'name');
335 $columns = $uq->getElementsByTagName(
'column');
336 foreach ($columns as $column) {
337 $current[
'COLUMNS'][] = $column->nodeValue;
340 $uniques[] = $current;
358 $indexesTag = $table->getElementsByTagName(
'indexes')->item(0);
361 if ($indexesTag !== NULL) {
362 $indexes = $indexesTag->getElementsByTagName(
'index');
363 foreach ($indexes as $index) {
365 $current[
'name'] = $index->getAttribute(
'name');
366 $current[
'COLUMNS'] = array();
368 $columns = $index->getElementsByTagName(
'column');
369 foreach ($columns as $column) {
370 $current[
'COLUMNS'][] = $column->nodeValue;
394 private static function _getColumnFromUdt(DomElement $column, array $udts)
396 $type = $column->getAttribute(
'type');
397 $newColumn[
'type'] = $udts[$type][
'type'];
398 $newColumn[
'size'] = $udts[$type][
'size'];
399 $newColumn[
'scale'] = $udts[$type][
'scale'];
419 $columnsTag = $table->getElementsByTagName(
'columns')->item(0);
420 if ($columnsTag !== NULL) {
421 $columns = $columnsTag->getElementsByTagName(
'column');
422 foreach ($columns as $column) {
426 if ((isset(self::$_udts[
'LOCAL']) === TRUE) && (in_array($column->getAttribute(
'type'), array_keys(self::$_udts[
'LOCAL'])) === TRUE)) {
428 $current = self::_getColumnFromUdt($column, self::$_udts[
'LOCAL']);
429 }
else if ((isset(self::$_udts[
'IMPORTED']) === TRUE) && (in_array($column->getAttribute(
'type'), array_keys(self::$_udts[
'IMPORTED'])) === TRUE)) {
434 $systemName = self::$_udts[
'IMPORTED'][$column->getAttribute(
'type')][
'system'];
437 if ($schemaDoc === NULL) {
438 $msg =
'Imported UDT from the system "'.$schemaName.
'" without a schema.xml';
442 $schemaNode = $schemaDoc->getElementsByTagName(
'schema')->item(0);
443 $udts = self::getSchemaUdts($schemaNode);
444 $current = self::_getColumnFromUdt($column, $udts[
'LOCAL']);
448 $current[
'type'] = $column->getAttribute(
'type');
449 $current[
'size'] = $column->getAttribute(
'size');
450 $current[
'scale'] = $column->getAttribute(
'scale');
454 $current[
'name'] = $column->nodeValue;
455 $current[
'allow-null'] = $column->getAttribute(
'allow-null');
456 $current[
'default'] = $column->getAttribute(
'default');
485 public static function validate(DomElement $schema)
488 if ($schema->tagName !==
'schema') {
493 $tables = $schema->getElementsByTagName(
'table');
496 self::validateSchemaUdts($schema);
498 foreach ($tables as $table) {
499 self::validateTable($table);
520 if ($table->getAttribute(
'name') ===
'') {
521 $msg =
'One of the tables does not contain "name" attribute.';
526 self::validateTableColumns($table);
529 self::validateTableConstraints($table);
532 self::validateTableIndexes($table);
535 self::validateTableSequences($table);
554 $columnsTag = $table->getElementsByTagName(
'columns')->item(0);
555 $tableName = $table->getAttribute(
'name');
556 if ($tableName ===
'') {
557 $tableName = $table->getAttribute(
'table');
560 if ($columnsTag === NULL) {
561 $msg = $tableName.
'\'s table has no columns.
';
562 throw new DALParserException($msg);
565 $cols = $columnsTag->getElementsByTagName('column
');
567 if ($cols->length === 0) {
568 $msg = $tableName.'\
's table has no columns.';
572 $msg = $tableName.
'\'s table column at #
';
574 // Check column attributes.
576 foreach ($cols as $col) {
577 $type = $col->getAttribute('type
');
579 $msg .= $attr.' does not have
"type" attribute.
';
580 throw new DALParserException($msg);
582 self::validSchemaColumnType($table, $type);
585 if (in_array($type, self::$_requiresSizeAttr) === TRUE) {
586 $size = $col->getAttribute('size
');
588 $msg .= $attr.' does not have
"size" attribute.
';
589 throw new DALParserException($msg);
593 // If type is NUMERIC then we need scale attribute.
594 if ($type === 'NUMERIC
') {
595 if ($col->getAttribute('scale
') === '') {
596 $msg .= $attr.' is type NUMERIC and it does not have
';
597 $msg .= ' "scale" attribute.
';
598 throw new DALParserException($msg);
602 if ($col->nodeValue === '') {
603 $msg .= $attr.' does not have a column name.
';
604 throw new DALParserException($msg);
610 }//end validateTableColumns()
624 public static function validSchemaColumnType(DomElement $table, $type)
626 // If type is not uppercase then its not valid.
627 if (strtoupper($type) !== $type) {
631 // Check if this is in available column types.
632 if (in_array($type, self::$_columnDataTypes) === TRUE) {
636 // Get the owner document.
637 $doc = $table->ownerDocument;
640 $xpath = new DomXpath($doc);
642 $result = $xpath->query($query);
644 if ($result->length > 0) {
667 $constTag = $table->getElementsByTagName(
'constraints')->item(0);
668 $tableName = $table->getAttribute(
'name');
669 $msg = $tableName.
'\'s table
';
671 if ($constTag === NULL) {
672 $msg .= ' does not have
"constraints".
';
673 throw new DALParserException($msg);
676 // Check general constraint rules.
677 self::validateConstraints($constTag, $msg);
679 // Additional Primary Key checks.
680 $pks = $constTag->getElementsByTagName('primary-key
');
681 if ($pks->length !== 1) {
682 $msg .= ' must have a primary-key.
';
683 throw new DALParserException($msg);
685 foreach ($pks as $pk) {
686 $cols = $pk->getElementsByTagName('column
');
687 // Each of the columns here must be defined in table columns.
688 foreach ($cols as $col) {
689 if (self::tableHasColumn($table, $col->nodeValue) === FALSE) {
690 $msg .= ' does not have column
"'.$col->nodeValue;
691 $msg .= '", but it was used in its primary-key.
';
692 throw new DALParserException($msg);
698 // Additional Foreign Key checks.
699 // Check if table has already defined this column name.
700 $fks = $constTag->getElementsByTagName('foreign-key
');
702 foreach ($fks as $fk) {
703 $cols = $fk->getElementsByTagName('column
');
704 if ($cols !== NULL) {
706 foreach ($cols as $col) {
707 $colVal = $col->nodeValue;
708 if (self::tableHasColumn($table, $colVal) === FALSE) {
709 $msg .= ' foreign-key #
'.$keyPosition;
710 $msg .= ' has column (#
'.$colLoc.')
';
711 $msg .= ' with column name that was NOT defined in
';
712 $msg .= ' table columns. Name:
'.$colVal;
713 throw new DALParserException($msg);
723 // Additional unique Key checks.
724 $uniques = $constTag->getElementsByTagName('unique
');
725 foreach ($uniques as $unique) {
726 $cols = $unique->getElementsByTagName('column
');
727 if ($cols !== NULL) {
728 foreach ($cols as $col) {
729 $colVal = $col->nodeValue;
730 if (self::tableHasColumn($table, $colVal) === FALSE) {
731 $msg .= ' does not have column
"'.$colVal;
732 $msg .= '", but it was used in its unique constraint.
';
733 throw new DALParserException($msg);
739 }//end validateTableConstraints()
751 public static function validateConstraints(DomElement $parent, $prefix='')
754 $pks = $parent->getElementsByTagName('primary-key
');
755 foreach ($pks as $pk) {
756 self::_validatePrimaryKeyConstraint($pk, $prefix);
760 $fks = $parent->getElementsByTagName('foreign-key
');
762 foreach ($fks as $fk) {
763 self::_validateForeignKeyConstraint($fk, $keyPosition, $prefix);
767 // Unique constraints.
768 $uniques = $parent->getElementsByTagName('unique
');
770 foreach ($uniques as $unique) {
771 self::_validateUniqueConstraint($unique, $keyPosition, $prefix);
775 }//end validateConstraints()
789 private static function _validatePrimaryKeyConstraint(DomElement $pk, $prefix='')
792 // Check that it has name attribute.
793 if ($pk->getAttribute('name
') === '') {
794 $msg .= ' primary-key does not have
"name" attribute.
';
795 throw new DALParserException($msg);
798 // Check that it has columns.
799 $cols = $pk->getElementsByTagName('column
');
800 if ($cols->length === 0) {
801 $msg .= ' has no columns defined
for its primary-key.
';
802 throw new DALParserException($msg);
805 }//end _validatePrimaryKeyConstraint()
819 private static function _validateForeignKeyConstraint(DomElement $fk, $keyPosition, $prefix='')
823 if ($fk->getAttribute('name
') === '') {
824 $msg .= ' foreign-key #
'.$keyPosition;
825 $msg .= ' does not have
"name" attribute.
';
826 throw new DALParserException($msg);
829 if ($fk->getAttribute('foreign-table
') === '') {
830 $msg .= ' foreign-key #
'.$keyPosition;
831 $msg .= ' does not have
"foreign-table" attribute.
';
832 throw new DALParserException($msg);
834 // Check that foreign-table exists?
837 // Check on-delete attribute value.
838 $onDelete = $fk->getAttribute('on-
delete');
839 if ($onDelete !== '') {
840 if (($onDelete !== 'CASCADE
') && ($onDelete !== 'NO ACTION
')) {
841 $msg .= ' foreign-key #
'.$keyPosition;
842 $msg .= ' has invalid on-
delete attribute value.
';
843 throw new DALParserException($msg);
847 // Check each column for this fk.
848 $cols = $fk->getElementsByTagName('column
');
849 if ($cols->length === 0) {
850 $msg .= ' foreign-key #
'.$keyPosition.' does not have columns.
';
851 throw new DALParserException($msg);
854 foreach ($cols as $col) {
855 if ($col->getAttribute('references
') === '') {
856 $msg .= ' foreign-key #
'.$keyPosition;
857 $msg .= ' has column (#
'.$colLoc.')
';
858 $msg .= ' with missing references attribute.
';
859 throw new DALParserException($msg);
861 // Check if foreign table has this column?
864 $colVal = $col->nodeValue;
865 if ($colVal === '') {
866 $msg .= ' foreign-key #
'.$keyPosition;
867 $msg .= ' has column (#
'.$colLoc.')
';
868 $msg .= ' with missing content.
';
869 throw new DALParserException($msg);
873 }//end foreach foreign-key columns
876 }//end _validateForeignKeyConstraint()
891 private static function _validateUniqueConstraint(DomElement $unique, $keyPosition, $prefix='')
895 if ($unique->getAttribute('name
') === '') {
896 $msg .= ' unique constraint #
'.$keyPosition;
897 $msg .= ' does not have
"name" attribute.
';
898 throw new DALParserException($msg);
901 $cols = $unique->getElementsByTagName('column
');
902 if ($cols->length === 0) {
903 $msg .= 'unique constraint #
'.$keyPosition.' does now have columns.
';
904 throw new DALParserException($msg);
907 }//end _validateUniqueConstraint()
923 public static function validateTableIndexes(DomElement $table)
925 $tableName = $table->getAttribute('name
');
926 $msg = $tableName.'\
's table';
929 $indexes = $table->getElementsByTagName(
'indexes');
930 if ($indexes->length !== 0) {
931 $indexes = $indexes->item(0)->getElementsByTagName(
'index');
932 foreach ($indexes as $index) {
933 if ($index->getAttribute(
'name') ===
'') {
934 $msg .=
' has index with missing "name" attribute.';
938 $cols = $index->getElementsByTagName(
'column');
939 if ($cols->length === 0) {
940 $msg .=
' has index with no columns.';
943 foreach ($cols as $col) {
944 $colVal = $col->nodeValue;
945 if (self::tableHasColumn($table, $colVal) === FALSE) {
946 $msg .=
' does not have column "'.$colVal;
947 $msg .=
'" but it was used in its index.';
972 $tableName = $table->getAttribute(
'name');
973 $msg = $tableName.
'\'s table
';
976 $seqsTag = $table->getElementsByTagName('sequences
');
977 if ($seqsTag->length !== 0) {
978 $seqs = $seqsTag->item(0)->getElementsByTagName('sequence
');
979 if ($seqs->length === 0) {
980 $msg .= ' has sequences tag with no sequence tags.
';
981 throw new DALParserException($msg);
984 foreach ($seqs as $seq) {
985 if ($seq->getAttribute('name
') === '') {
986 $msg .= ' has sequence with missing
"name" attribute.
';
987 throw new DALParserException($msg);
992 }//end validateTableSequences()
1006 public static function validateSchemaUdts(DomElement $schema)
1008 $msg = 'Schema
"'.$schema->getAttribute('system').'" ';
1010 // Get schema User Defined Types (UDTs).
1011 $udtsTag = $schema->getElementsByTagName('udts
');
1013 if ($udtsTag->length !== 0) {
1015 $ludts = $udtsTag->item(0)->getElementsByTagName('udt
');
1017 if ($ludts->length !== 0) {
1018 foreach ($ludts as $ludt) {
1019 // UDTs must be in uppercase.
1020 $var = $ludt->nodeValue;
1021 if ($var !== strtoupper($var)) {
1022 $msg .= ' has UDT with an invalid name
"'.$var.'".
';
1023 $msg .= ' Expected
"'.strtoupper($var).'".
';
1024 throw new DALParserException($msg);
1027 $type = $ludt->getAttribute('type
');
1029 $msg .= ' has UDT with no type attribute.
';
1030 throw new DALParserException($msg);
1033 if (in_array($type, self::$_requiresSizeAttr) === TRUE) {
1034 if ($ludt->getAttribute('size
') === '') {
1035 $msg .= ' has
'.$type.' UDT with no size attr.
';
1036 throw new DALParserException($msg);
1040 if ($type === 'NUMERIC
') {
1041 if ($ludt->getAttribute('scale
') === '') {
1042 $msg .= ' has NUMERIC UDT with no scale attr.
';
1043 throw new DALParserException($msg);
1046 }//end foreach ludts
1049 // Get foreign UDTs.
1050 $fudts = $udtsTag->item(0)->getElementsByTagName('udt-
import');
1051 if ($fudts->length !== 0) {
1052 foreach ($fudts as $fudt) {
1053 if ($fudt->getAttribute('system
') === '') {
1054 $msg .= ' has foreign UDT with no
"system" attribute
';
1055 throw new DALParserException($msg);
1058 $var = $fudt->nodeValue;
1059 if ($var !== strtoupper($var)) {
1060 $msg .= ' has foreign UDT with an invalid var name.
';
1061 throw new DALParserException($msg);
1068 }//end validateSchemaUdts()
1082 public static function tableHasColumn(DomElement $table, $columnName)
1084 // Is this a table node?
1085 if ($table->tagName !== 'table
') {
1095 $cols = $table->getElementsByTagName(
'columns')->item(0);
1096 if ($cols !== NULL) {
1097 $cols = $cols->getElementsByTagName(
'column');
1098 foreach ($cols as $col) {
1099 if ($col->nodeValue === $columnName) {