Exception
2 years ago
Visitor
9 months ago
AbstractAsset.php
2 years ago
AbstractSchemaManager.php
9 months ago
Column.php
2 years ago
ColumnDiff.php
2 years ago
Comparator.php
2 years ago
Constraint.php
2 years ago
DB2SchemaManager.php
2 years ago
DefaultSchemaManagerFactory.php
2 years ago
ForeignKeyConstraint.php
9 months ago
Identifier.php
2 years ago
Index.php
2 years ago
LegacySchemaManagerFactory.php
2 years ago
MySQLSchemaManager.php
9 months ago
OracleSchemaManager.php
2 years ago
PostgreSQLSchemaManager.php
9 months ago
SQLServerSchemaManager.php
9 months ago
Schema.php
2 years ago
SchemaConfig.php
2 years ago
SchemaDiff.php
2 years ago
SchemaException.php
2 years ago
SchemaManagerFactory.php
2 years ago
Sequence.php
2 years ago
SqliteSchemaManager.php
8 months ago
Table.php
2 years ago
TableDiff.php
2 years ago
UniqueConstraint.php
2 years ago
View.php
2 years ago
OracleSchemaManager.php
335 lines
| 1 | <?php |
| 2 | namespace MailPoetVendor\Doctrine\DBAL\Schema; |
| 3 | if (!defined('ABSPATH')) exit; |
| 4 | use MailPoetVendor\Doctrine\DBAL\Exception; |
| 5 | use MailPoetVendor\Doctrine\DBAL\Platforms\OraclePlatform; |
| 6 | use MailPoetVendor\Doctrine\DBAL\Result; |
| 7 | use MailPoetVendor\Doctrine\DBAL\Types\Type; |
| 8 | use MailPoetVendor\Doctrine\Deprecations\Deprecation; |
| 9 | use function array_change_key_case; |
| 10 | use function array_values; |
| 11 | use function implode; |
| 12 | use function is_string; |
| 13 | use function preg_match; |
| 14 | use function str_replace; |
| 15 | use function strpos; |
| 16 | use function strtolower; |
| 17 | use function strtoupper; |
| 18 | use function trim; |
| 19 | use const CASE_LOWER; |
| 20 | class OracleSchemaManager extends AbstractSchemaManager |
| 21 | { |
| 22 | public function listTableNames() |
| 23 | { |
| 24 | return $this->doListTableNames(); |
| 25 | } |
| 26 | public function listTables() |
| 27 | { |
| 28 | return $this->doListTables(); |
| 29 | } |
| 30 | public function listTableDetails($name) |
| 31 | { |
| 32 | Deprecation::triggerIfCalledFromOutside('doctrine/dbal', 'https://github.com/doctrine/dbal/pull/5595', '%s is deprecated. Use introspectTable() instead.', __METHOD__); |
| 33 | return $this->doListTableDetails($name); |
| 34 | } |
| 35 | public function listTableColumns($table, $database = null) |
| 36 | { |
| 37 | return $this->doListTableColumns($table, $database); |
| 38 | } |
| 39 | public function listTableIndexes($table) |
| 40 | { |
| 41 | return $this->doListTableIndexes($table); |
| 42 | } |
| 43 | public function listTableForeignKeys($table, $database = null) |
| 44 | { |
| 45 | return $this->doListTableForeignKeys($table, $database); |
| 46 | } |
| 47 | protected function _getPortableViewDefinition($view) |
| 48 | { |
| 49 | $view = array_change_key_case($view, CASE_LOWER); |
| 50 | return new View($this->getQuotedIdentifierName($view['view_name']), $view['text']); |
| 51 | } |
| 52 | protected function _getPortableTableDefinition($table) |
| 53 | { |
| 54 | $table = array_change_key_case($table, CASE_LOWER); |
| 55 | return $this->getQuotedIdentifierName($table['table_name']); |
| 56 | } |
| 57 | protected function _getPortableTableIndexesList($tableIndexes, $tableName = null) |
| 58 | { |
| 59 | $indexBuffer = []; |
| 60 | foreach ($tableIndexes as $tableIndex) { |
| 61 | $tableIndex = array_change_key_case($tableIndex, CASE_LOWER); |
| 62 | $keyName = strtolower($tableIndex['name']); |
| 63 | $buffer = []; |
| 64 | if ($tableIndex['is_primary'] === 'P') { |
| 65 | $keyName = 'primary'; |
| 66 | $buffer['primary'] = \true; |
| 67 | $buffer['non_unique'] = \false; |
| 68 | } else { |
| 69 | $buffer['primary'] = \false; |
| 70 | $buffer['non_unique'] = !$tableIndex['is_unique']; |
| 71 | } |
| 72 | $buffer['key_name'] = $keyName; |
| 73 | $buffer['column_name'] = $this->getQuotedIdentifierName($tableIndex['column_name']); |
| 74 | $indexBuffer[] = $buffer; |
| 75 | } |
| 76 | return parent::_getPortableTableIndexesList($indexBuffer, $tableName); |
| 77 | } |
| 78 | protected function _getPortableTableColumnDefinition($tableColumn) |
| 79 | { |
| 80 | $tableColumn = array_change_key_case($tableColumn, CASE_LOWER); |
| 81 | $dbType = strtolower($tableColumn['data_type']); |
| 82 | if (strpos($dbType, 'timestamp(') === 0) { |
| 83 | if (strpos($dbType, 'with time zone') !== \false) { |
| 84 | $dbType = 'timestamptz'; |
| 85 | } else { |
| 86 | $dbType = 'timestamp'; |
| 87 | } |
| 88 | } |
| 89 | $unsigned = $fixed = $precision = $scale = $length = null; |
| 90 | if (!isset($tableColumn['column_name'])) { |
| 91 | $tableColumn['column_name'] = ''; |
| 92 | } |
| 93 | // Default values returned from database sometimes have trailing spaces. |
| 94 | if (is_string($tableColumn['data_default'])) { |
| 95 | $tableColumn['data_default'] = trim($tableColumn['data_default']); |
| 96 | } |
| 97 | if ($tableColumn['data_default'] === '' || $tableColumn['data_default'] === 'NULL') { |
| 98 | $tableColumn['data_default'] = null; |
| 99 | } |
| 100 | if ($tableColumn['data_default'] !== null) { |
| 101 | // Default values returned from database are represented as literal expressions |
| 102 | if (preg_match('/^\'(.*)\'$/s', $tableColumn['data_default'], $matches) === 1) { |
| 103 | $tableColumn['data_default'] = str_replace("''", "'", $matches[1]); |
| 104 | } |
| 105 | } |
| 106 | if ($tableColumn['data_precision'] !== null) { |
| 107 | $precision = (int) $tableColumn['data_precision']; |
| 108 | } |
| 109 | if ($tableColumn['data_scale'] !== null) { |
| 110 | $scale = (int) $tableColumn['data_scale']; |
| 111 | } |
| 112 | $type = $this->_platform->getDoctrineTypeMapping($dbType); |
| 113 | $type = $this->extractDoctrineTypeFromComment($tableColumn['comments'], $type); |
| 114 | $tableColumn['comments'] = $this->removeDoctrineTypeFromComment($tableColumn['comments'], $type); |
| 115 | switch ($dbType) { |
| 116 | case 'number': |
| 117 | if ($precision === 20 && $scale === 0) { |
| 118 | $type = 'bigint'; |
| 119 | } elseif ($precision === 5 && $scale === 0) { |
| 120 | $type = 'smallint'; |
| 121 | } elseif ($precision === 1 && $scale === 0) { |
| 122 | $type = 'boolean'; |
| 123 | } elseif ($scale > 0) { |
| 124 | $type = 'decimal'; |
| 125 | } |
| 126 | break; |
| 127 | case 'varchar': |
| 128 | case 'varchar2': |
| 129 | case 'nvarchar2': |
| 130 | $length = $tableColumn['char_length']; |
| 131 | $fixed = \false; |
| 132 | break; |
| 133 | case 'raw': |
| 134 | $length = $tableColumn['data_length']; |
| 135 | $fixed = \true; |
| 136 | break; |
| 137 | case 'char': |
| 138 | case 'nchar': |
| 139 | $length = $tableColumn['char_length']; |
| 140 | $fixed = \true; |
| 141 | break; |
| 142 | } |
| 143 | $options = ['notnull' => $tableColumn['nullable'] === 'N', 'fixed' => (bool) $fixed, 'unsigned' => (bool) $unsigned, 'default' => $tableColumn['data_default'], 'length' => $length, 'precision' => $precision, 'scale' => $scale, 'comment' => isset($tableColumn['comments']) && $tableColumn['comments'] !== '' ? $tableColumn['comments'] : null]; |
| 144 | return new Column($this->getQuotedIdentifierName($tableColumn['column_name']), Type::getType($type), $options); |
| 145 | } |
| 146 | protected function _getPortableTableForeignKeysList($tableForeignKeys) |
| 147 | { |
| 148 | $list = []; |
| 149 | foreach ($tableForeignKeys as $value) { |
| 150 | $value = array_change_key_case($value, CASE_LOWER); |
| 151 | if (!isset($list[$value['constraint_name']])) { |
| 152 | if ($value['delete_rule'] === 'NO ACTION') { |
| 153 | $value['delete_rule'] = null; |
| 154 | } |
| 155 | $list[$value['constraint_name']] = ['name' => $this->getQuotedIdentifierName($value['constraint_name']), 'local' => [], 'foreign' => [], 'foreignTable' => $value['references_table'], 'onDelete' => $value['delete_rule']]; |
| 156 | } |
| 157 | $localColumn = $this->getQuotedIdentifierName($value['local_column']); |
| 158 | $foreignColumn = $this->getQuotedIdentifierName($value['foreign_column']); |
| 159 | $list[$value['constraint_name']]['local'][$value['position']] = $localColumn; |
| 160 | $list[$value['constraint_name']]['foreign'][$value['position']] = $foreignColumn; |
| 161 | } |
| 162 | return parent::_getPortableTableForeignKeysList($list); |
| 163 | } |
| 164 | protected function _getPortableTableForeignKeyDefinition($tableForeignKey) : ForeignKeyConstraint |
| 165 | { |
| 166 | return new ForeignKeyConstraint(array_values($tableForeignKey['local']), $this->getQuotedIdentifierName($tableForeignKey['foreignTable']), array_values($tableForeignKey['foreign']), $this->getQuotedIdentifierName($tableForeignKey['name']), ['onDelete' => $tableForeignKey['onDelete']]); |
| 167 | } |
| 168 | protected function _getPortableSequenceDefinition($sequence) |
| 169 | { |
| 170 | $sequence = array_change_key_case($sequence, CASE_LOWER); |
| 171 | return new Sequence($this->getQuotedIdentifierName($sequence['sequence_name']), (int) $sequence['increment_by'], (int) $sequence['min_value']); |
| 172 | } |
| 173 | protected function _getPortableDatabaseDefinition($database) |
| 174 | { |
| 175 | $database = array_change_key_case($database, CASE_LOWER); |
| 176 | return $database['username']; |
| 177 | } |
| 178 | public function createDatabase($database) |
| 179 | { |
| 180 | $statement = $this->_platform->getCreateDatabaseSQL($database); |
| 181 | $params = $this->_conn->getParams(); |
| 182 | if (isset($params['password'])) { |
| 183 | $statement .= ' IDENTIFIED BY ' . $params['password']; |
| 184 | } |
| 185 | $this->_conn->executeStatement($statement); |
| 186 | $statement = 'GRANT DBA TO ' . $database; |
| 187 | $this->_conn->executeStatement($statement); |
| 188 | } |
| 189 | public function dropAutoincrement($table) |
| 190 | { |
| 191 | $sql = $this->_platform->getDropAutoincrementSql($table); |
| 192 | foreach ($sql as $query) { |
| 193 | $this->_conn->executeStatement($query); |
| 194 | } |
| 195 | return \true; |
| 196 | } |
| 197 | public function dropTable($name) |
| 198 | { |
| 199 | $this->tryMethod('dropAutoincrement', $name); |
| 200 | parent::dropTable($name); |
| 201 | } |
| 202 | private function getQuotedIdentifierName($identifier) : string |
| 203 | { |
| 204 | if (preg_match('/[a-z]/', $identifier) === 1) { |
| 205 | return $this->_platform->quoteIdentifier($identifier); |
| 206 | } |
| 207 | return $identifier; |
| 208 | } |
| 209 | protected function selectTableNames(string $databaseName) : Result |
| 210 | { |
| 211 | $sql = <<<'SQL' |
| 212 | SELECT TABLE_NAME |
| 213 | FROM ALL_TABLES |
| 214 | WHERE OWNER = :OWNER |
| 215 | ORDER BY TABLE_NAME |
| 216 | SQL; |
| 217 | return $this->_conn->executeQuery($sql, ['OWNER' => $databaseName]); |
| 218 | } |
| 219 | protected function selectTableColumns(string $databaseName, ?string $tableName = null) : Result |
| 220 | { |
| 221 | $sql = 'SELECT'; |
| 222 | if ($tableName === null) { |
| 223 | $sql .= ' C.TABLE_NAME,'; |
| 224 | } |
| 225 | $sql .= <<<'SQL' |
| 226 | C.COLUMN_NAME, |
| 227 | C.DATA_TYPE, |
| 228 | C.DATA_DEFAULT, |
| 229 | C.DATA_PRECISION, |
| 230 | C.DATA_SCALE, |
| 231 | C.CHAR_LENGTH, |
| 232 | C.DATA_LENGTH, |
| 233 | C.NULLABLE, |
| 234 | D.COMMENTS |
| 235 | FROM ALL_TAB_COLUMNS C |
| 236 | INNER JOIN ALL_TABLES T |
| 237 | ON T.OWNER = C.OWNER |
| 238 | AND T.TABLE_NAME = C.TABLE_NAME |
| 239 | LEFT JOIN ALL_COL_COMMENTS D |
| 240 | ON D.OWNER = C.OWNER |
| 241 | AND D.TABLE_NAME = C.TABLE_NAME |
| 242 | AND D.COLUMN_NAME = C.COLUMN_NAME |
| 243 | SQL; |
| 244 | $conditions = ['C.OWNER = :OWNER']; |
| 245 | $params = ['OWNER' => $databaseName]; |
| 246 | if ($tableName !== null) { |
| 247 | $conditions[] = 'C.TABLE_NAME = :TABLE_NAME'; |
| 248 | $params['TABLE_NAME'] = $tableName; |
| 249 | } |
| 250 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY C.COLUMN_ID'; |
| 251 | return $this->_conn->executeQuery($sql, $params); |
| 252 | } |
| 253 | protected function selectIndexColumns(string $databaseName, ?string $tableName = null) : Result |
| 254 | { |
| 255 | $sql = 'SELECT'; |
| 256 | if ($tableName === null) { |
| 257 | $sql .= ' IND_COL.TABLE_NAME,'; |
| 258 | } |
| 259 | $sql .= <<<'SQL' |
| 260 | IND_COL.INDEX_NAME AS NAME, |
| 261 | IND.INDEX_TYPE AS TYPE, |
| 262 | DECODE(IND.UNIQUENESS, 'NONUNIQUE', 0, 'UNIQUE', 1) AS IS_UNIQUE, |
| 263 | IND_COL.COLUMN_NAME, |
| 264 | IND_COL.COLUMN_POSITION AS COLUMN_POS, |
| 265 | CON.CONSTRAINT_TYPE AS IS_PRIMARY |
| 266 | FROM ALL_IND_COLUMNS IND_COL |
| 267 | LEFT JOIN ALL_INDEXES IND |
| 268 | ON IND.OWNER = IND_COL.INDEX_OWNER |
| 269 | AND IND.INDEX_NAME = IND_COL.INDEX_NAME |
| 270 | LEFT JOIN ALL_CONSTRAINTS CON |
| 271 | ON CON.OWNER = IND_COL.INDEX_OWNER |
| 272 | AND CON.INDEX_NAME = IND_COL.INDEX_NAME |
| 273 | SQL; |
| 274 | $conditions = ['IND_COL.INDEX_OWNER = :OWNER']; |
| 275 | $params = ['OWNER' => $databaseName]; |
| 276 | if ($tableName !== null) { |
| 277 | $conditions[] = 'IND_COL.TABLE_NAME = :TABLE_NAME'; |
| 278 | $params['TABLE_NAME'] = $tableName; |
| 279 | } |
| 280 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY IND_COL.TABLE_NAME, IND_COL.INDEX_NAME' . ', IND_COL.COLUMN_POSITION'; |
| 281 | return $this->_conn->executeQuery($sql, $params); |
| 282 | } |
| 283 | protected function selectForeignKeyColumns(string $databaseName, ?string $tableName = null) : Result |
| 284 | { |
| 285 | $sql = 'SELECT'; |
| 286 | if ($tableName === null) { |
| 287 | $sql .= ' COLS.TABLE_NAME,'; |
| 288 | } |
| 289 | $sql .= <<<'SQL' |
| 290 | ALC.CONSTRAINT_NAME, |
| 291 | ALC.DELETE_RULE, |
| 292 | COLS.COLUMN_NAME LOCAL_COLUMN, |
| 293 | COLS.POSITION, |
| 294 | R_COLS.TABLE_NAME REFERENCES_TABLE, |
| 295 | R_COLS.COLUMN_NAME FOREIGN_COLUMN |
| 296 | FROM ALL_CONS_COLUMNS COLS |
| 297 | LEFT JOIN ALL_CONSTRAINTS ALC ON ALC.OWNER = COLS.OWNER AND ALC.CONSTRAINT_NAME = COLS.CONSTRAINT_NAME |
| 298 | LEFT JOIN ALL_CONS_COLUMNS R_COLS ON R_COLS.OWNER = ALC.R_OWNER AND |
| 299 | R_COLS.CONSTRAINT_NAME = ALC.R_CONSTRAINT_NAME AND |
| 300 | R_COLS.POSITION = COLS.POSITION |
| 301 | SQL; |
| 302 | $conditions = ["ALC.CONSTRAINT_TYPE = 'R'", 'COLS.OWNER = :OWNER']; |
| 303 | $params = ['OWNER' => $databaseName]; |
| 304 | if ($tableName !== null) { |
| 305 | $conditions[] = 'COLS.TABLE_NAME = :TABLE_NAME'; |
| 306 | $params['TABLE_NAME'] = $tableName; |
| 307 | } |
| 308 | $sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY COLS.TABLE_NAME, COLS.CONSTRAINT_NAME' . ', COLS.POSITION'; |
| 309 | return $this->_conn->executeQuery($sql, $params); |
| 310 | } |
| 311 | protected function fetchTableOptionsByTable(string $databaseName, ?string $tableName = null) : array |
| 312 | { |
| 313 | $sql = 'SELECT TABLE_NAME, COMMENTS'; |
| 314 | $conditions = ['OWNER = :OWNER']; |
| 315 | $params = ['OWNER' => $databaseName]; |
| 316 | if ($tableName !== null) { |
| 317 | $conditions[] = 'TABLE_NAME = :TABLE_NAME'; |
| 318 | $params['TABLE_NAME'] = $tableName; |
| 319 | } |
| 320 | $sql .= ' FROM ALL_TAB_COMMENTS WHERE ' . implode(' AND ', $conditions); |
| 321 | $metadata = $this->_conn->executeQuery($sql, $params)->fetchAllAssociativeIndexed(); |
| 322 | $tableOptions = []; |
| 323 | foreach ($metadata as $table => $data) { |
| 324 | $data = array_change_key_case($data, CASE_LOWER); |
| 325 | $tableOptions[$table] = ['comment' => $data['comments']]; |
| 326 | } |
| 327 | return $tableOptions; |
| 328 | } |
| 329 | protected function normalizeName(string $name) : string |
| 330 | { |
| 331 | $identifier = new Identifier($name); |
| 332 | return $identifier->isQuoted() ? $identifier->getName() : strtoupper($name); |
| 333 | } |
| 334 | } |
| 335 |