SchemaEncoder.php 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2024 Robin Appelman <robin@icewind.nl>
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Core\Command\Db;
  8. use Doctrine\DBAL\Platforms\AbstractMySQLPlatform;
  9. use Doctrine\DBAL\Platforms\AbstractPlatform;
  10. use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
  11. use Doctrine\DBAL\Schema\Schema;
  12. use Doctrine\DBAL\Schema\Table;
  13. use Doctrine\DBAL\Types\PhpIntegerMappingType;
  14. use Doctrine\DBAL\Types\Type;
  15. class SchemaEncoder {
  16. /**
  17. * Encode a DBAL schema to json, performing some normalization based on the database platform
  18. *
  19. * @param Schema $schema
  20. * @param AbstractPlatform $platform
  21. * @return array
  22. */
  23. public function encodeSchema(Schema $schema, AbstractPlatform $platform): array {
  24. $encoded = ['tables' => [], 'sequences' => []];
  25. foreach ($schema->getTables() as $table) {
  26. $encoded[$table->getName()] = $this->encodeTable($table, $platform);
  27. }
  28. ksort($encoded);
  29. return $encoded;
  30. }
  31. /**
  32. * @psalm-type ColumnArrayType =
  33. */
  34. private function encodeTable(Table $table, AbstractPlatform $platform): array {
  35. $encoded = ['columns' => [], 'indexes' => []];
  36. foreach ($table->getColumns() as $column) {
  37. /**
  38. * @var array{
  39. * name: string,
  40. * default: mixed,
  41. * notnull: bool,
  42. * length: ?int,
  43. * precision: int,
  44. * scale: int,
  45. * unsigned: bool,
  46. * fixed: bool,
  47. * autoincrement: bool,
  48. * comment: string,
  49. * columnDefinition: ?string,
  50. * collation?: string,
  51. * charset?: string,
  52. * jsonb?: bool,
  53. * } $data
  54. **/
  55. $data = $column->toArray();
  56. $data['type'] = Type::getTypeRegistry()->lookupName($column->getType());
  57. $data['default'] = $column->getType()->convertToPHPValue($column->getDefault(), $platform);
  58. if ($platform instanceof PostgreSQLPlatform) {
  59. $data['unsigned'] = false;
  60. if ($column->getType() instanceof PhpIntegerMappingType) {
  61. $data['length'] = null;
  62. }
  63. unset($data['jsonb']);
  64. } elseif ($platform instanceof AbstractMySqlPlatform) {
  65. if ($column->getType() instanceof PhpIntegerMappingType) {
  66. $data['length'] = null;
  67. } elseif (in_array($data['type'], ['text', 'blob', 'datetime', 'float', 'json'])) {
  68. $data['length'] = 0;
  69. }
  70. unset($data['collation']);
  71. unset($data['charset']);
  72. }
  73. if ($data['type'] === 'string' && $data['length'] === null) {
  74. $data['length'] = 255;
  75. }
  76. $encoded['columns'][$column->getName()] = $data;
  77. }
  78. ksort($encoded['columns']);
  79. foreach ($table->getIndexes() as $index) {
  80. $options = $index->getOptions();
  81. if (isset($options['lengths']) && count(array_filter($options['lengths'])) === 0) {
  82. unset($options['lengths']);
  83. }
  84. if ($index->isPrimary()) {
  85. if ($platform instanceof PostgreSqlPlatform) {
  86. $name = $table->getName() . '_pkey';
  87. } elseif ($platform instanceof AbstractMySQLPlatform) {
  88. $name = 'PRIMARY';
  89. } else {
  90. $name = $index->getName();
  91. }
  92. } else {
  93. $name = $index->getName();
  94. }
  95. if ($platform instanceof PostgreSqlPlatform) {
  96. $name = strtolower($name);
  97. }
  98. $encoded['indexes'][$name] = [
  99. 'name' => $name,
  100. 'columns' => $index->getColumns(),
  101. 'unique' => $index->isUnique(),
  102. 'primary' => $index->isPrimary(),
  103. 'flags' => $index->getFlags(),
  104. 'options' => $options,
  105. ];
  106. }
  107. ksort($encoded['indexes']);
  108. return $encoded;
  109. }
  110. }