2012-10-10 22:49:47 +04:00
< ? php
/**
2016-07-21 18:07:57 +03:00
* @ copyright Copyright ( c ) 2016 , ownCloud , Inc .
*
2015-03-26 13:44:34 +03:00
* @ author Bart Visscher < bartv @ thisnet . nl >
2020-03-31 11:49:10 +03:00
* @ author Christoph Wurst < christoph @ winzerhof - wurst . at >
2015-03-26 13:44:34 +03:00
* @ author Morris Jobke < hey @ morrisjobke . de >
* @ author Oliver Gasser < oliver . gasser @ gmail . com >
2016-07-21 19:13:36 +03:00
* @ author Robin Appelman < robin @ icewind . nl >
2016-01-12 17:02:16 +03:00
* @ author Robin McCorkell < robin @ mccorkell . me . uk >
2015-03-26 13:44:34 +03:00
* @ author Thomas Müller < thomas . mueller @ tmit . eu >
* @ author Victor Dubiniuk < dubiniuk @ owncloud . com >
2020-12-16 16:54:15 +03:00
* @ author Vincent Petry < vincent @ nextcloud . com >
2015-03-26 13:44:34 +03:00
*
* @ license AGPL - 3.0
*
* This code is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License , version 3 ,
* as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License , version 3 ,
2019-12-03 21:57:53 +03:00
* along with this program . If not , see < http :// www . gnu . org / licenses />
2015-03-26 13:44:34 +03:00
*
2012-10-10 22:49:47 +04:00
*/
2015-02-26 13:37:37 +03:00
2013-07-29 18:33:00 +04:00
namespace OC\DB ;
2012-10-10 22:49:47 +04:00
2014-11-19 02:25:26 +03:00
use Doctrine\DBAL\Platforms\AbstractPlatform ;
2017-03-19 23:52:54 +03:00
use Doctrine\DBAL\Schema\Schema ;
2014-11-19 02:25:26 +03:00
use OCP\IConfig ;
2013-07-29 18:33:00 +04:00
class MDB2SchemaReader {
/**
* @ var string $DBTABLEPREFIX
*/
protected $DBTABLEPREFIX ;
/**
* @ var \Doctrine\DBAL\Platforms\AbstractPlatform $platform
*/
protected $platform ;
2015-07-30 14:57:04 +03:00
/** @var IConfig */
protected $config ;
2013-07-29 18:33:00 +04:00
/**
2014-11-19 02:25:26 +03:00
* @ param \OCP\IConfig $config
2013-07-29 18:33:00 +04:00
* @ param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
*/
2014-11-19 02:25:26 +03:00
public function __construct ( IConfig $config , AbstractPlatform $platform ) {
2013-07-29 18:33:00 +04:00
$this -> platform = $platform ;
2015-07-30 14:57:04 +03:00
$this -> config = $config ;
2014-11-19 02:25:26 +03:00
$this -> DBTABLEPREFIX = $config -> getSystemValue ( 'dbtableprefix' , 'oc_' );
2013-07-29 18:33:00 +04:00
}
/**
* @ param string $file
2015-07-30 11:57:16 +03:00
* @ param Schema $schema
2017-03-19 23:52:54 +03:00
* @ return Schema
2013-07-29 18:33:00 +04:00
* @ throws \DomainException
2013-03-19 21:52:54 +04:00
*/
2015-07-30 11:57:16 +03:00
public function loadSchemaFromFile ( $file , Schema $schema ) {
2014-03-03 14:31:46 +04:00
$loadEntities = libxml_disable_entity_loader ( false );
2012-10-10 22:49:47 +04:00
$xml = simplexml_load_file ( $file );
2014-03-03 14:31:46 +04:00
libxml_disable_entity_loader ( $loadEntities );
2013-07-29 18:33:00 +04:00
foreach ( $xml -> children () as $child ) {
/**
* @ var \SimpleXMLElement $child
*/
switch ( $child -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'name' :
case 'create' :
case 'overwrite' :
case 'charset' :
break ;
case 'table' :
2013-07-29 18:33:00 +04:00
$this -> loadTable ( $schema , $child );
2012-10-10 22:49:47 +04:00
break ;
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $child -> getName ());
2012-10-10 22:49:47 +04:00
}
}
return $schema ;
}
2013-03-19 21:52:54 +04:00
/**
2014-05-12 00:51:30 +04:00
* @ param \Doctrine\DBAL\Schema\Schema $schema
2013-07-29 18:33:00 +04:00
* @ param \SimpleXMLElement $xml
* @ throws \DomainException
2013-03-19 21:52:54 +04:00
*/
2013-07-29 18:33:00 +04:00
private function loadTable ( $schema , $xml ) {
$table = null ;
foreach ( $xml -> children () as $child ) {
/**
* @ var \SimpleXMLElement $child
*/
switch ( $child -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'name' :
$name = ( string ) $child ;
2013-07-29 18:33:00 +04:00
$name = str_replace ( '*dbprefix*' , $this -> DBTABLEPREFIX , $name );
$name = $this -> platform -> quoteIdentifier ( $name );
2012-10-10 22:49:47 +04:00
$table = $schema -> createTable ( $name );
break ;
case 'create' :
case 'overwrite' :
case 'charset' :
break ;
case 'declaration' :
2013-07-29 18:33:00 +04:00
if ( is_null ( $table )) {
throw new \DomainException ( 'Table declaration before table name' );
}
$this -> loadDeclaration ( $table , $child );
2012-10-10 22:49:47 +04:00
break ;
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $child -> getName ());
2012-10-10 22:49:47 +04:00
}
}
}
2013-03-19 21:52:54 +04:00
/**
* @ param \Doctrine\DBAL\Schema\Table $table
2013-07-29 18:33:00 +04:00
* @ param \SimpleXMLElement $xml
* @ throws \DomainException
2013-03-19 21:52:54 +04:00
*/
2013-07-29 18:33:00 +04:00
private function loadDeclaration ( $table , $xml ) {
foreach ( $xml -> children () as $child ) {
/**
* @ var \SimpleXMLElement $child
*/
switch ( $child -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'field' :
2013-07-29 18:33:00 +04:00
$this -> loadField ( $table , $child );
2012-10-10 22:49:47 +04:00
break ;
case 'index' :
2013-07-29 18:33:00 +04:00
$this -> loadIndex ( $table , $child );
2012-10-10 22:49:47 +04:00
break ;
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $child -> getName ());
2012-10-10 22:49:47 +04:00
}
}
}
2013-07-29 18:33:00 +04:00
/**
* @ param \Doctrine\DBAL\Schema\Table $table
* @ param \SimpleXMLElement $xml
* @ throws \DomainException
*/
private function loadField ( $table , $xml ) {
2020-03-26 11:30:18 +03:00
$options = [ 'notnull' => false ];
2013-07-29 18:33:00 +04:00
foreach ( $xml -> children () as $child ) {
/**
* @ var \SimpleXMLElement $child
*/
switch ( $child -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'name' :
$name = ( string ) $child ;
2013-07-29 18:33:00 +04:00
$name = $this -> platform -> quoteIdentifier ( $name );
2012-10-10 22:49:47 +04:00
break ;
case 'type' :
$type = ( string ) $child ;
2013-07-29 18:33:00 +04:00
switch ( $type ) {
2012-10-10 22:49:47 +04:00
case 'text' :
$type = 'string' ;
break ;
case 'clob' :
$type = 'text' ;
break ;
case 'timestamp' :
$type = 'datetime' ;
break ;
2013-11-12 16:55:06 +04:00
case 'numeric' :
$type = 'decimal' ;
break ;
2012-10-10 22:49:47 +04:00
}
break ;
case 'length' :
$length = ( string ) $child ;
$options [ 'length' ] = $length ;
break ;
case 'unsigned' :
2013-07-29 18:33:00 +04:00
$unsigned = $this -> asBool ( $child );
2012-10-10 22:49:47 +04:00
$options [ 'unsigned' ] = $unsigned ;
break ;
case 'notnull' :
2013-07-29 18:33:00 +04:00
$notnull = $this -> asBool ( $child );
2012-10-10 22:49:47 +04:00
$options [ 'notnull' ] = $notnull ;
break ;
case 'autoincrement' :
2013-07-29 18:33:00 +04:00
$autoincrement = $this -> asBool ( $child );
2012-10-10 22:49:47 +04:00
$options [ 'autoincrement' ] = $autoincrement ;
break ;
case 'default' :
$default = ( string ) $child ;
$options [ 'default' ] = $default ;
break ;
2013-07-05 23:42:37 +04:00
case 'comments' :
$comment = ( string ) $child ;
$options [ 'comment' ] = $comment ;
break ;
2013-07-22 18:25:07 +04:00
case 'primary' :
2013-07-29 18:33:00 +04:00
$primary = $this -> asBool ( $child );
2013-07-22 18:25:07 +04:00
$options [ 'primary' ] = $primary ;
break ;
2013-12-18 01:46:45 +04:00
case 'precision' :
$precision = ( string ) $child ;
$options [ 'precision' ] = $precision ;
break ;
case 'scale' :
$scale = ( string ) $child ;
$options [ 'scale' ] = $scale ;
break ;
2012-10-10 22:49:47 +04:00
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $child -> getName ());
2012-10-10 22:49:47 +04:00
}
}
if ( isset ( $name ) && isset ( $type )) {
2013-11-11 20:58:21 +04:00
if ( isset ( $options [ 'default' ]) && empty ( $options [ 'default' ])) {
2013-03-17 18:25:41 +04:00
if ( empty ( $options [ 'notnull' ]) || ! $options [ 'notnull' ]) {
unset ( $options [ 'default' ]);
2013-06-25 00:37:07 +04:00
$options [ 'notnull' ] = false ;
2013-07-18 22:28:57 +04:00
} else {
$options [ 'default' ] = '' ;
2013-03-17 18:25:41 +04:00
}
2013-11-12 16:47:47 +04:00
if ( $type == 'integer' || $type == 'decimal' ) {
2013-03-17 18:25:41 +04:00
$options [ 'default' ] = 0 ;
2013-07-29 18:33:00 +04:00
} elseif ( $type == 'boolean' ) {
$options [ 'default' ] = false ;
2012-10-10 22:49:47 +04:00
}
2013-03-17 18:25:41 +04:00
if ( ! empty ( $options [ 'autoincrement' ]) && $options [ 'autoincrement' ]) {
2012-10-10 22:49:47 +04:00
unset ( $options [ 'default' ]);
}
}
2013-07-29 18:33:00 +04:00
if ( $type === 'integer' && isset ( $options [ 'default' ])) {
$options [ 'default' ] = ( int ) $options [ 'default' ];
}
if ( $type === 'integer' && isset ( $options [ 'length' ])) {
2012-10-10 22:49:47 +04:00
$length = $options [ 'length' ];
2013-03-17 20:34:35 +04:00
if ( $length < 4 ) {
2012-10-10 22:49:47 +04:00
$type = 'smallint' ;
2020-04-10 11:35:09 +03:00
} elseif ( $length > 4 ) {
2012-10-10 22:49:47 +04:00
$type = 'bigint' ;
}
}
2013-07-29 18:33:00 +04:00
if ( $type === 'boolean' && isset ( $options [ 'default' ])) {
$options [ 'default' ] = $this -> asBool ( $options [ 'default' ]);
2013-07-22 18:24:46 +04:00
}
2012-10-10 22:49:47 +04:00
if ( ! empty ( $options [ 'autoincrement' ])
2013-07-29 18:33:00 +04:00
&& ! empty ( $options [ 'notnull' ])
) {
2013-07-18 22:28:57 +04:00
$options [ 'primary' ] = true ;
}
2017-03-19 23:52:54 +03:00
2021-01-06 21:39:41 +03:00
# not used anymore in the options argument
# see https://github.com/doctrine/dbal/commit/138eb85234a1faeaa2e6a32cd7bcc66bb51c64e8#diff-300f55366adb50a32a40882ebdc95c163b141f64cba5f45f20bda04a907b3eb3L82
# therefore it's read before and then unset right before the addColumn call
$setPrimaryKey = false ;
2013-07-18 22:28:57 +04:00
if ( ! empty ( $options [ 'primary' ]) && $options [ 'primary' ]) {
2021-01-06 21:39:41 +03:00
$setPrimaryKey = true ;
}
unset ( $options [ 'primary' ]);
$table -> addColumn ( $name , $type , $options );
if ( $setPrimaryKey ) {
2020-03-26 11:30:18 +03:00
$table -> setPrimaryKey ([ $name ]);
2012-10-10 22:49:47 +04:00
}
}
}
2013-07-29 18:33:00 +04:00
/**
* @ param \Doctrine\DBAL\Schema\Table $table
* @ param \SimpleXMLElement $xml
* @ throws \DomainException
*/
private function loadIndex ( $table , $xml ) {
2012-10-10 22:49:47 +04:00
$name = null ;
2020-03-26 11:30:18 +03:00
$fields = [];
2013-07-29 18:33:00 +04:00
foreach ( $xml -> children () as $child ) {
/**
* @ var \SimpleXMLElement $child
*/
switch ( $child -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'name' :
$name = ( string ) $child ;
break ;
case 'primary' :
2013-07-29 18:33:00 +04:00
$primary = $this -> asBool ( $child );
2012-10-10 22:49:47 +04:00
break ;
case 'unique' :
2013-07-29 18:33:00 +04:00
$unique = $this -> asBool ( $child );
2012-10-10 22:49:47 +04:00
break ;
case 'field' :
2013-07-29 18:33:00 +04:00
foreach ( $child -> children () as $field ) {
/**
* @ var \SimpleXMLElement $field
*/
switch ( $field -> getName ()) {
2012-10-10 22:49:47 +04:00
case 'name' :
$field_name = ( string ) $field ;
2013-07-29 18:33:00 +04:00
$field_name = $this -> platform -> quoteIdentifier ( $field_name );
2012-10-10 22:49:47 +04:00
$fields [] = $field_name ;
break ;
case 'sorting' :
break ;
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $field -> getName ());
2012-10-10 22:49:47 +04:00
}
}
break ;
default :
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Unknown element: ' . $child -> getName ());
2012-10-10 22:49:47 +04:00
}
}
if ( ! empty ( $fields )) {
if ( isset ( $primary ) && $primary ) {
2015-02-11 19:35:46 +03:00
if ( $table -> hasPrimaryKey ()) {
return ;
}
2012-10-10 22:49:47 +04:00
$table -> setPrimaryKey ( $fields , $name );
2013-09-13 19:45:27 +04:00
} else {
2013-07-29 18:33:00 +04:00
if ( isset ( $unique ) && $unique ) {
$table -> addUniqueIndex ( $fields , $name );
} else {
$table -> addIndex ( $fields , $name );
}
2013-09-13 19:45:27 +04:00
}
2012-10-10 22:49:47 +04:00
} else {
2013-07-29 18:33:00 +04:00
throw new \DomainException ( 'Empty index definition: ' . $name . ' options:' . print_r ( $fields , true ));
2012-10-10 22:49:47 +04:00
}
}
2013-07-29 18:33:00 +04:00
/**
2014-05-11 21:28:45 +04:00
* @ param \SimpleXMLElement | string $xml
2013-07-29 18:33:00 +04:00
* @ return bool
*/
private function asBool ( $xml ) {
2012-10-10 22:49:47 +04:00
$result = ( string ) $xml ;
if ( $result == 'true' ) {
$result = true ;
2013-07-29 18:33:00 +04:00
} elseif ( $result == 'false' ) {
2012-10-10 22:49:47 +04:00
$result = false ;
}
return ( bool ) $result ;
}
}