1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027 |
- <?php /* vim: se et ts=4 sw=4 sts=4 fdm=marker tw=80: */
- /**
- * Copyright (c) 1998-2010 Manuel Lemos, Tomas V.V.Cox,
- * Stig. S. Bakken, Lukas Smith, Igor Feghali
- * All rights reserved.
- *
- * MDB2_Schema enables users to maintain RDBMS independant schema files
- * in XML that can be used to manipulate both data and database schemas
- * This LICENSE is in the BSD license style.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,
- * Lukas Smith, Igor Feghali nor the names of his contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
- * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * PHP version 5
- *
- * @category Database
- * @package MDB2_Schema
- * @author Christian Dickmann <dickmann@php.net>
- * @author Igor Feghali <ifeghali@php.net>
- * @license BSD http://www.opensource.org/licenses/bsd-license.php
- * @version SVN: $Id$
- * @link http://pear.php.net/packages/MDB2_Schema
- */
- /**
- * Validates an XML schema file
- *
- * @category Database
- * @package MDB2_Schema
- * @author Igor Feghali <ifeghali@php.net>
- * @license BSD http://www.opensource.org/licenses/bsd-license.php
- * @link http://pear.php.net/packages/MDB2_Schema
- */
- class MDB2_Schema_Validate
- {
- // {{{ properties
- var $fail_on_invalid_names = true;
- var $valid_types = array();
- var $force_defaults = true;
- var $max_identifiers_length = null;
- // }}}
- // {{{ constructor
- /**
- * PHP 5 constructor
- *
- * @param bool $fail_on_invalid_names array with reserved words per RDBMS
- * @param array $valid_types information of all valid fields
- * types
- * @param bool $force_defaults if true sets a default value to
- * field when not explicit
- * @param int $max_identifiers_length maximum allowed size for entities
- * name
- *
- * @return void
- *
- * @access public
- * @static
- */
- function __construct($fail_on_invalid_names = true, $valid_types = array(),
- $force_defaults = true, $max_identifiers_length = null
- ) {
- if (empty($GLOBALS['_MDB2_Schema_Reserved'])) {
- $GLOBALS['_MDB2_Schema_Reserved'] = array();
- }
- if (is_array($fail_on_invalid_names)) {
- $this->fail_on_invalid_names = array_intersect($fail_on_invalid_names,
- array_keys($GLOBALS['_MDB2_Schema_Reserved']));
- } elseif ($fail_on_invalid_names === true) {
- $this->fail_on_invalid_names = array_keys($GLOBALS['_MDB2_Schema_Reserved']);
- } else {
- $this->fail_on_invalid_names = array();
- }
- $this->valid_types = $valid_types;
- $this->force_defaults = $force_defaults;
- $this->max_identifiers_length = $max_identifiers_length;
- }
- /**
- * PHP 4 compatible constructor
- *
- * @param bool $fail_on_invalid_names array with reserved words per RDBMS
- * @param array $valid_types information of all valid fields
- * types
- * @param bool $force_defaults if true sets a default value to
- * field when not explicit
- * @param int $max_identifiers_length maximum allowed size for entities
- * name
- *
- * @return void
- *
- * @access public
- * @static
- */
- function MDB2_Schema_Validate($fail_on_invalid_names = true, $valid_types = array(),
- $force_defaults = true, $max_identifiers_length = null
- ) {
- $this->__construct($fail_on_invalid_names, $valid_types, $force_defaults);
- }
- // }}}
- // {{{ raiseError()
- /**
- * Pushes a MDB2_Schema_Error into stack and returns it
- *
- * @param int $ecode MDB2_Schema's error code
- * @param string $msg textual message
- *
- * @return object
- * @access private
- * @static
- */
- function &raiseError($ecode, $msg = null)
- {
- $error = MDB2_Schema::raiseError($ecode, null, null, $msg);
- return $error;
- }
- // }}}
- // {{{ isBoolean()
- /**
- * Verifies if a given value can be considered boolean. If yes, set value
- * to true or false according to its actual contents and return true. If
- * not, keep its contents untouched and return false.
- *
- * @param mixed &$value value to be checked
- *
- * @return bool
- *
- * @access public
- * @static
- */
- function isBoolean(&$value)
- {
- if (is_bool($value)) {
- return true;
- }
- if ($value === 0 || $value === 1 || $value === '') {
- $value = (bool)$value;
- return true;
- }
- if (!is_string($value)) {
- return false;
- }
- switch ($value) {
- case '0':
- case 'N':
- case 'n':
- case 'no':
- case 'false':
- $value = false;
- break;
- case '1':
- case 'Y':
- case 'y':
- case 'yes':
- case 'true':
- $value = true;
- break;
- default:
- return false;
- }
- return true;
- }
- // }}}
- // {{{ validateTable()
- /* Definition */
- /**
- * Checks whether the definition of a parsed table is valid. Modify table
- * definition when necessary.
- *
- * @param array $tables multi dimensional array that contains the
- * tables of current database.
- * @param array &$table multi dimensional array that contains the
- * structure and optional data of the table.
- * @param string $table_name name of the parsed table
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateTable($tables, &$table, $table_name)
- {
- /* Table name duplicated? */
- if (is_array($tables) && isset($tables[$table_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'table "'.$table_name.'" already exists');
- }
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($table_name, 'table');
- if (PEAR::isError($result)) {
- return $result;
- }
- /* Was */
- if (empty($table['was'])) {
- $table['was'] = $table_name;
- }
- /* Have we got fields? */
- if (empty($table['fields']) || !is_array($table['fields'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'tables need one or more fields');
- }
- /* Autoincrement */
- $autoinc = $primary = false;
- foreach ($table['fields'] as $field_name => $field) {
- if (!empty($field['autoincrement'])) {
- if ($autoinc) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'there was already an autoincrement field in "'.$table_name.'" before "'.$field_name.'"');
- }
- $autoinc = $field_name;
- }
- }
- /*
- * Checking Indexes
- * this have to be done here otherwise we can't
- * guarantee that all table fields were already
- * defined in the moment we are parsing indexes
- */
- if (!empty($table['indexes']) && is_array($table['indexes'])) {
- foreach ($table['indexes'] as $name => $index) {
- $skip_index = false;
- if (!empty($index['primary'])) {
- /*
- * Lets see if we should skip this index since there is
- * already an auto increment on this field this implying
- * a primary key index.
- */
- if (count($index['fields']) == '1'
- && $autoinc
- && array_key_exists($autoinc, $index['fields'])) {
- $skip_index = true;
- } elseif ($autoinc || $primary) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'there was already an primary index or autoincrement field in "'.$table_name.'" before "'.$name.'"');
- } else {
- $primary = true;
- }
- }
- if (!$skip_index && is_array($index['fields'])) {
- foreach ($index['fields'] as $field_name => $field) {
- if (!isset($table['fields'][$field_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'index field "'.$field_name.'" does not exist');
- }
- if (!empty($index['primary'])
- && !$table['fields'][$field_name]['notnull']
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'all primary key fields must be defined notnull in "'.$table_name.'"');
- }
- }
- } else {
- unset($table['indexes'][$name]);
- }
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateField()
- /**
- * Checks whether the definition of a parsed field is valid. Modify field
- * definition when necessary.
- *
- * @param array $fields multi dimensional array that contains the
- * fields of current table.
- * @param array &$field multi dimensional array that contains the
- * structure of the parsed field.
- * @param string $field_name name of the parsed field
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateField($fields, &$field, $field_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($field_name, 'field');
- if (PEAR::isError($result)) {
- return $result;
- }
- /* Field name duplicated? */
- if (is_array($fields) && isset($fields[$field_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "'.$field_name.'" already exists');
- }
- /* Type check */
- if (empty($field['type'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'no field type specified');
- }
- if (!empty($this->valid_types) && !array_key_exists($field['type'], $this->valid_types)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'no valid field type ("'.$field['type'].'") specified');
- }
- /* Unsigned */
- if (array_key_exists('unsigned', $field) && !$this->isBoolean($field['unsigned'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'unsigned has to be a boolean value');
- }
- /* Fixed */
- if (array_key_exists('fixed', $field) && !$this->isBoolean($field['fixed'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'fixed has to be a boolean value');
- }
- /* Length */
- if (array_key_exists('length', $field) && $field['length'] <= 0) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'length has to be an integer greater 0');
- }
- // if it's a DECIMAL datatype, check if a 'scale' value is provided:
- // <length>8,4</length> should be translated to DECIMAL(8,4)
- if (is_float($this->valid_types[$field['type']])
- && !empty($field['length'])
- && strpos($field['length'], ',') !== false
- ) {
- list($field['length'], $field['scale']) = explode(',', $field['length']);
- }
- /* Was */
- if (empty($field['was'])) {
- $field['was'] = $field_name;
- }
- /* Notnull */
- if (empty($field['notnull'])) {
- $field['notnull'] = false;
- }
- if (!$this->isBoolean($field['notnull'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "notnull" has to be a boolean value');
- }
- /* Default */
- if ($this->force_defaults
- && !array_key_exists('default', $field)
- && $field['type'] != 'clob' && $field['type'] != 'blob'
- ) {
- $field['default'] = $this->valid_types[$field['type']];
- }
- if (array_key_exists('default', $field)) {
- if ($field['type'] == 'clob' || $field['type'] == 'blob') {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field['type'].'"-fields are not allowed to have a default value');
- }
- if ($field['default'] === '' && !$field['notnull']) {
- $field['default'] = null;
- }
- }
- if (isset($field['default'])
- && PEAR::isError($result = $this->validateDataFieldValue($field, $field['default'], $field_name))
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'default value of "'.$field_name.'" is incorrect: '.$result->getUserinfo());
- }
- /* Autoincrement */
- if (!empty($field['autoincrement'])) {
- if (!$field['notnull']) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'all autoincrement fields must be defined notnull');
- }
- if (empty($field['default'])) {
- $field['default'] = '0';
- } elseif ($field['default'] !== '0' && $field['default'] !== 0) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'all autoincrement fields must be defined default "0"');
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateIndex()
- /**
- * Checks whether a parsed index is valid. Modify index definition when
- * necessary.
- *
- * @param array $table_indexes multi dimensional array that contains the
- * indexes of current table.
- * @param array &$index multi dimensional array that contains the
- * structure of the parsed index.
- * @param string $index_name name of the parsed index
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateIndex($table_indexes, &$index, $index_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($index_name, 'index');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($table_indexes) && isset($table_indexes[$index_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'index "'.$index_name.'" already exists');
- }
- if (array_key_exists('unique', $index) && !$this->isBoolean($index['unique'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "unique" has to be a boolean value');
- }
- if (array_key_exists('primary', $index) && !$this->isBoolean($index['primary'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "primary" has to be a boolean value');
- }
- /* Have we got fields? */
- if (empty($index['fields']) || !is_array($index['fields'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'indexes need one or more fields');
- }
- if (empty($index['was'])) {
- $index['was'] = $index_name;
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateIndexField()
- /**
- * Checks whether a parsed index-field is valid. Modify its definition when
- * necessary.
- *
- * @param array $index_fields multi dimensional array that contains the
- * fields of current index.
- * @param array &$field multi dimensional array that contains the
- * structure of the parsed index-field.
- * @param string $field_name name of the parsed index-field
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateIndexField($index_fields, &$field, $field_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($field_name, 'index field');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($index_fields) && isset($index_fields[$field_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'index field "'.$field_name.'" already exists');
- }
- if (empty($field['sorting'])) {
- $field['sorting'] = 'ascending';
- } elseif ($field['sorting'] !== 'ascending' && $field['sorting'] !== 'descending') {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'sorting type unknown');
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateConstraint()
- /**
- * Checks whether a parsed foreign key is valid. Modify its definition when
- * necessary.
- *
- * @param array $table_constraints multi dimensional array that contains the
- * constraints of current table.
- * @param array &$constraint multi dimensional array that contains the
- * structure of the parsed foreign key.
- * @param string $constraint_name name of the parsed foreign key
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateConstraint($table_constraints, &$constraint, $constraint_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($constraint_name, 'foreign key');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($table_constraints) && isset($table_constraints[$constraint_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign key "'.$constraint_name.'" already exists');
- }
- /* Have we got fields? */
- if (empty($constraint['fields']) || !is_array($constraint['fields'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign key "'.$constraint_name.'" need one or more fields');
- }
- /* Have we got referenced fields? */
- if (empty($constraint['references']) || !is_array($constraint['references'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign key "'.$constraint_name.'" need to reference one or more fields');
- }
- /* Have we got referenced table? */
- if (empty($constraint['references']['table'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign key "'.$constraint_name.'" need to reference a table');
- }
- if (empty($constraint['was'])) {
- $constraint['was'] = $constraint_name;
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateConstraintField()
- /**
- * Checks whether a foreign-field is valid.
- *
- * @param array $constraint_fields multi dimensional array that contains the
- * fields of current foreign key.
- * @param string $field_name name of the parsed foreign-field
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateConstraintField($constraint_fields, $field_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($field_name, 'foreign key field');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($constraint_fields) && isset($constraint_fields[$field_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign field "'.$field_name.'" already exists');
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateConstraintReferencedField()
- /**
- * Checks whether a foreign-referenced field is valid.
- *
- * @param array $referenced_fields multi dimensional array that contains the
- * fields of current foreign key.
- * @param string $field_name name of the parsed foreign-field
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateConstraintReferencedField($referenced_fields, $field_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($field_name, 'referenced foreign field');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($referenced_fields) && isset($referenced_fields[$field_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'foreign field "'.$field_name.'" already referenced');
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateSequence()
- /**
- * Checks whether the definition of a parsed sequence is valid. Modify
- * sequence definition when necessary.
- *
- * @param array $sequences multi dimensional array that contains the
- * sequences of current database.
- * @param array &$sequence multi dimensional array that contains the
- * structure of the parsed sequence.
- * @param string $sequence_name name of the parsed sequence
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateSequence($sequences, &$sequence, $sequence_name)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($sequence_name, 'sequence');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($sequences) && isset($sequences[$sequence_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'sequence "'.$sequence_name.'" already exists');
- }
- if (is_array($this->fail_on_invalid_names)) {
- $name = strtoupper($sequence_name);
- foreach ($this->fail_on_invalid_names as $rdbms) {
- if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'sequence name "'.$sequence_name.'" is a reserved word in: '.$rdbms);
- }
- }
- }
- if (empty($sequence['was'])) {
- $sequence['was'] = $sequence_name;
- }
- if (!empty($sequence['on'])
- && (empty($sequence['on']['table']) || empty($sequence['on']['field']))
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'sequence "'.$sequence_name.'" on a table was not properly defined');
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateDatabase()
- /**
- * Checks whether a parsed database is valid. Modify its structure and
- * data when necessary.
- *
- * @param array &$database multi dimensional array that contains the
- * structure and optional data of the database.
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateDatabase(&$database)
- {
- if (!is_array($database)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'something wrong went with database definition');
- }
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($database['name'], 'database');
- if (PEAR::isError($result)) {
- return $result;
- }
- /* Create */
- if (isset($database['create'])
- && !$this->isBoolean($database['create'])
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "create" has to be a boolean value');
- }
- /* Overwrite */
- if (isset($database['overwrite'])
- && $database['overwrite'] !== ''
- && !$this->isBoolean($database['overwrite'])
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "overwrite" has to be a boolean value');
- }
- /*
- * This have to be done here otherwise we can't guarantee that all
- * tables were already defined in the moment we are parsing constraints
- */
- if (isset($database['tables'])) {
- foreach ($database['tables'] as $table_name => $table) {
- if (!empty($table['constraints'])) {
- foreach ($table['constraints'] as $constraint_name => $constraint) {
- $referenced_table_name = $constraint['references']['table'];
- if (!isset($database['tables'][$referenced_table_name])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'referenced table "'.$referenced_table_name.'" of foreign key "'.$constraint_name.'" of table "'.$table_name.'" does not exist');
- }
- if (empty($constraint['references']['fields'])) {
- $referenced_table = $database['tables'][$referenced_table_name];
- $primary = false;
- if (!empty($referenced_table['indexes'])) {
- foreach ($referenced_table['indexes'] as $index_name => $index) {
- if (array_key_exists('primary', $index)
- && $index['primary']
- ) {
- $primary = array();
- foreach ($index['fields'] as $field_name => $field) {
- $primary[$field_name] = '';
- }
- break;
- }
- }
- }
- if (!$primary) {
- foreach ($referenced_table['fields'] as $field_name => $field) {
- if (array_key_exists('autoincrement', $field)
- && $field['autoincrement']
- ) {
- $primary = array( $field_name => '' );
- break;
- }
- }
- }
- if (!$primary) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'referenced table "'.$referenced_table_name.'" has no primary key and no referenced field was specified for foreign key "'.$constraint_name.'" of table "'.$table_name.'"');
- }
- $constraint['references']['fields'] = $primary;
- }
- /* the same number of referencing and referenced fields ? */
- if (count($constraint['fields']) != count($constraint['references']['fields'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'The number of fields in the referenced key must match those of the foreign key "'.$constraint_name.'"');
- }
- $database['tables'][$table_name]['constraints'][$constraint_name]['references']['fields'] = $constraint['references']['fields'];
- }
- }
- }
- }
- /*
- * This have to be done here otherwise we can't guarantee that all
- * tables were already defined in the moment we are parsing sequences
- */
- if (isset($database['sequences'])) {
- foreach ($database['sequences'] as $seq_name => $seq) {
- if (!empty($seq['on'])
- && empty($database['tables'][$seq['on']['table']]['fields'][$seq['on']['field']])
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'sequence "'.$seq_name.'" was assigned on unexisting field/table');
- }
- }
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateDataField()
- /* Data Manipulation */
- /**
- * Checks whether a parsed DML-field is valid. Modify its structure when
- * necessary. This is called when validating INSERT and
- * UPDATE.
- *
- * @param array $table_fields multi dimensional array that contains the
- * definition for current table's fields.
- * @param array $instruction_fields multi dimensional array that contains the
- * parsed fields of the current DML instruction.
- * @param string &$field array that contains the parsed instruction field
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateDataField($table_fields, $instruction_fields, &$field)
- {
- /**
- * Valid name ?
- */
- $result = $this->validateIdentifier($field['name'], 'field');
- if (PEAR::isError($result)) {
- return $result;
- }
- if (is_array($instruction_fields) && isset($instruction_fields[$field['name']])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'field "'.$field['name'].'" already initialized');
- }
- if (is_array($table_fields) && !isset($table_fields[$field['name']])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field['name'].'" is not defined');
- }
- if (!isset($field['group']['type'])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field['name'].'" has no initial value');
- }
- if (isset($field['group']['data'])
- && $field['group']['type'] == 'value'
- && $field['group']['data'] !== ''
- && PEAR::isError($result = $this->validateDataFieldValue($table_fields[$field['name']], $field['group']['data'], $field['name']))
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- 'value of "'.$field['name'].'" is incorrect: '.$result->getUserinfo());
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateDataFieldValue()
- /**
- * Checks whether a given value is compatible with a table field. This is
- * done when parsing a field for a INSERT or UPDATE instruction.
- *
- * @param array $field_def multi dimensional array that contains the
- * definition for current table's fields.
- * @param string &$field_value value to fill the parsed field
- * @param string $field_name name of the parsed field
- *
- * @return bool|error object
- *
- * @access public
- * @see MDB2_Schema_Validate::validateInsertField()
- */
- function validateDataFieldValue($field_def, &$field_value, $field_name)
- {
- switch ($field_def['type']) {
- case 'text':
- case 'clob':
- if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is larger than "'.$field_def['length'].'"');
- }
- break;
- case 'blob':
- $field_value = pack('H*', $field_value);
- if (!empty($field_def['length']) && strlen($field_value) > $field_def['length']) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is larger than "'.$field_def['type'].'"');
- }
- break;
- case 'integer':
- if ($field_value != ((int)$field_value)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- //$field_value = (int)$field_value;
- if (!empty($field_def['unsigned']) && $field_def['unsigned'] && $field_value < 0) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" signed instead of unsigned');
- }
- break;
- case 'boolean':
- if (!$this->isBoolean($field_value)) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- break;
- case 'date':
- if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})/', $field_value)
- && $field_value !== 'CURRENT_DATE'
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- break;
- case 'timestamp':
- if (!preg_match('/([0-9]{4})-([0-9]{1,2})-([0-9]{1,2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $field_value)
- && strcasecmp($field_value, 'now()') != 0
- && $field_value !== 'CURRENT_TIMESTAMP'
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- break;
- case 'time':
- if (!preg_match("/([0-9]{2}):([0-9]{2}):([0-9]{2})/", $field_value)
- && $field_value !== 'CURRENT_TIME'
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- break;
- case 'float':
- case 'double':
- if ($field_value != (double)$field_value) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- '"'.$field_value.'" is not of type "'.$field_def['type'].'"');
- }
- //$field_value = (double)$field_value;
- break;
- }
- return MDB2_OK;
- }
- // }}}
- // {{{ validateIdentifier()
- /**
- * Checks whether a given identifier is valid for current driver.
- *
- * @param string $id identifier to check
- * @param string $type whether identifier represents a table name, index, etc.
- *
- * @return bool|error object
- *
- * @access public
- */
- function validateIdentifier($id, $type)
- {
- $max_length = $this->max_identifiers_length;
- $cur_length = strlen($id);
- /**
- * Have we got a name?
- */
- if (!$id) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- "a $type has to have a name");
- }
- /**
- * Supported length ?
- */
- if ($max_length !== null
- && $cur_length > $max_length
- ) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- "$type name '$id' is too long for current driver");
- } elseif ($cur_length > 30) {
- // FIXME: find a way to issue a warning in MDB2_Schema object
- /* $this->warnings[] = "$type name '$id' might not be
- portable to other drivers"; */
- }
- /**
- * Reserved ?
- */
- if (is_array($this->fail_on_invalid_names)) {
- $name = strtoupper($id);
- foreach ($this->fail_on_invalid_names as $rdbms) {
- if (in_array($name, $GLOBALS['_MDB2_Schema_Reserved'][$rdbms])) {
- return $this->raiseError(MDB2_SCHEMA_ERROR_VALIDATE,
- "$type name '$id' is a reserved word in: $rdbms");
- }
- }
- }
- return MDB2_OK;
- }
- // }}}
- }
|