1<?php 2/** 3 * @author Bart Visscher <bartv@thisnet.nl> 4 * @author Jörn Friedrich Dreyer <jfd@butonic.de> 5 * @author Morris Jobke <hey@morrisjobke.de> 6 * @author tbelau666 <thomas.belau@gmx.de> 7 * @author Thomas Müller <thomas.mueller@tmit.eu> 8 * 9 * @copyright Copyright (c) 2018, ownCloud GmbH 10 * @license AGPL-3.0 11 * 12 * This code is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU Affero General Public License, version 3, 14 * as published by the Free Software Foundation. 15 * 16 * This program is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU Affero General Public License for more details. 20 * 21 * You should have received a copy of the GNU Affero General Public License, version 3, 22 * along with this program. If not, see <http://www.gnu.org/licenses/> 23 * 24 */ 25 26namespace OC\DB; 27 28use Doctrine\DBAL\Schema\Column; 29use Doctrine\DBAL\Schema\Index; 30 31class MDB2SchemaWriter { 32 33 /** 34 * @param string $file 35 * @param \OC\DB\Connection $conn 36 * @return bool 37 */ 38 public static function saveSchemaToFile($file, \OC\DB\Connection $conn) { 39 $config = \OC::$server->getConfig(); 40 41 $xml = new \SimpleXMLElement('<database/>'); 42 $xml->addChild('name', $config->getSystemValue('dbname', 'owncloud')); 43 $xml->addChild('create', 'true'); 44 $xml->addChild('overwrite', 'false'); 45 if ($config->getSystemValue('dbtype', 'sqlite') === 'mysql' && $config->getSystemValue('mysql.utf8mb4', false)) { 46 $xml->addChild('charset', 'utf8mb4'); 47 } else { 48 $xml->addChild('charset', 'utf8'); 49 } 50 51 // FIX ME: bloody work around 52 if ($config->getSystemValue('dbtype', 'sqlite') === 'oci') { 53 $filterExpression = '/^"' . \preg_quote($conn->getPrefix()) . '/'; 54 } else { 55 $filterExpression = '/^' . \preg_quote($conn->getPrefix()) . '/'; 56 } 57 $conn->getConfiguration()->setFilterSchemaAssetsExpression($filterExpression); 58 59 foreach ($conn->getSchemaManager()->listTables() as $table) { 60 self::saveTable($table, $xml->addChild('table')); 61 } 62 \file_put_contents($file, $xml->asXML()); 63 return true; 64 } 65 66 /** 67 * @param \Doctrine\DBAL\Schema\Table $table 68 * @param \SimpleXMLElement $xml 69 */ 70 private static function saveTable($table, $xml) { 71 $xml->addChild('name', $table->getName()); 72 $declaration = $xml->addChild('declaration'); 73 foreach ($table->getColumns() as $column) { 74 self::saveColumn($column, $declaration->addChild('field')); 75 } 76 foreach ($table->getIndexes() as $index) { 77 if ($index->getName() == 'PRIMARY') { 78 $autoincrement = false; 79 foreach ($index->getColumns() as $column) { 80 if ($table->getColumn($column)->getAutoincrement()) { 81 $autoincrement = true; 82 } 83 } 84 if ($autoincrement) { 85 continue; 86 } 87 } 88 self::saveIndex($index, $declaration->addChild('index')); 89 } 90 } 91 92 /** 93 * @param Column $column 94 * @param \SimpleXMLElement $xml 95 */ 96 private static function saveColumn($column, $xml) { 97 $xml->addChild('name', $column->getName()); 98 switch ($column->getType()) { 99 case 'SmallInt': 100 case 'Integer': 101 case 'BigInt': 102 $xml->addChild('type', 'integer'); 103 $default = $column->getDefault(); 104 if ($default === null && $column->getAutoincrement()) { 105 $default = '0'; 106 } 107 $xml->addChild('default', $default); 108 $xml->addChild('notnull', self::toBool($column->getNotnull())); 109 if ($column->getAutoincrement()) { 110 $xml->addChild('autoincrement', '1'); 111 } 112 if ($column->getUnsigned()) { 113 $xml->addChild('unsigned', 'true'); 114 } 115 $length = '4'; 116 if ($column->getType() == 'SmallInt') { 117 $length = '2'; 118 } elseif ($column->getType() == 'BigInt') { 119 $length = '8'; 120 } 121 $xml->addChild('length', $length); 122 break; 123 case 'String': 124 $xml->addChild('type', 'text'); 125 $default = \trim($column->getDefault()); 126 if ($default === '') { 127 $default = false; 128 } 129 $xml->addChild('default', $default); 130 $xml->addChild('notnull', self::toBool($column->getNotnull())); 131 $xml->addChild('length', $column->getLength()); 132 break; 133 case 'Text': 134 $xml->addChild('type', 'clob'); 135 $xml->addChild('notnull', self::toBool($column->getNotnull())); 136 break; 137 case 'Decimal': 138 $xml->addChild('type', 'decimal'); 139 $xml->addChild('default', $column->getDefault()); 140 $xml->addChild('notnull', self::toBool($column->getNotnull())); 141 $xml->addChild('length', '15'); 142 break; 143 case 'Boolean': 144 $xml->addChild('type', 'integer'); 145 $xml->addChild('default', $column->getDefault()); 146 $xml->addChild('notnull', self::toBool($column->getNotnull())); 147 $xml->addChild('length', '1'); 148 break; 149 case 'DateTime': 150 $xml->addChild('type', 'timestamp'); 151 $xml->addChild('default', $column->getDefault()); 152 $xml->addChild('notnull', self::toBool($column->getNotnull())); 153 break; 154 155 } 156 } 157 158 /** 159 * @param Index $index 160 * @param \SimpleXMLElement $xml 161 */ 162 private static function saveIndex($index, $xml) { 163 $xml->addChild('name', $index->getName()); 164 if ($index->isPrimary()) { 165 $xml->addChild('primary', 'true'); 166 } elseif ($index->isUnique()) { 167 $xml->addChild('unique', 'true'); 168 } 169 foreach ($index->getColumns() as $column) { 170 $field = $xml->addChild('field'); 171 $field->addChild('name', $column); 172 $field->addChild('sorting', 'ascending'); 173 } 174 } 175 176 private static function toBool($bool) { 177 return $bool ? 'true' : 'false'; 178 } 179} 180