Passed
Push — 6.0 ( d42c5f...ee4eb8 )
by Olivier
01:47
created

render_column_constraint()   B

Complexity

Conditions 9
Paths 48

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 8
c 1
b 0
f 0
dl 0
loc 16
rs 8.0555
cc 9
nc 48
nop 1
1
<?php
2
3
namespace ICanBoogie\ActiveRecord\Driver;
4
5
use ICanBoogie\ActiveRecord\Schema;
6
use ICanBoogie\ActiveRecord\Schema\BelongsTo;
7
use ICanBoogie\ActiveRecord\Schema\Binary;
8
use ICanBoogie\ActiveRecord\Schema\Blob;
9
use ICanBoogie\ActiveRecord\Schema\Boolean;
10
use ICanBoogie\ActiveRecord\Schema\Column;
11
use ICanBoogie\ActiveRecord\Schema\Date;
12
use ICanBoogie\ActiveRecord\Schema\DateTime;
13
use ICanBoogie\ActiveRecord\Schema\Integer;
14
use ICanBoogie\ActiveRecord\Schema\Serial;
15
use ICanBoogie\ActiveRecord\Schema\Text;
16
use ICanBoogie\ActiveRecord\Schema\Time;
17
18
use InvalidArgumentException;
19
20
use function implode;
21
use function in_array;
22
use function is_array;
23
use function PHPUnit\Framework\matches;
24
25
/**
26
 * @see https://www.sqlite.org/lang_createtable.html
27
 */
28
class TableRendererForPostgreSQL extends TableRenderer
29
{
30
    protected function render_column_defs(Schema $schema): array
31
    {
32
        $render = [];
33
34
        foreach ($schema->columns as $name => $column) {
35
            $type = $this->render_type_name($column);
36
            $constraint = $this->render_column_constraint($column);
37
38
            $render[] = "$name $type $constraint";
39
        }
40
41
        return $render;
42
    }
43
44
    protected function render_type_name(Column $column): string
45
    {
46
        return match ($column::class) {
47
            Serial::class => match ($column->size) {
48
                Integer::SIZE_SMALL => "SMALLSERIAL",
49
                Integer::SIZE_REGULAR => "SERIAL",
50
                Integer::SIZE_BIG => "BIGSERIAL",
51
                default => throw new InvalidArgumentException("Serial size {$column->size} is not supported by PostgreSQL")
52
            },
53
            BelongsTo::class, Integer::class => match ($column->size) {
54
                Integer::SIZE_SMALL => "SMALLINT",
55
                Integer::SIZE_REGULAR => "INTEGER",
56
                Integer::SIZE_BIG => "BIGINT",
57
                default => throw new InvalidArgumentException("Integer size {$column->size} is not supported by PostgreSQL")
58
            },
59
60
            // https://www.postgresql.org/docs/current/datatype-bit.html
61
            Binary::class => $column->fixed
62
                ? "BIT($column->size)"
63
                : "BIT VARYING($column->size)",
64
65
            // https://www.postgresql.org/docs/current/datatype-character.html
66
            Text::class => "TEXT",
67
            // https://www.postgresql.org/docs/current/datatype-binary.html
68
            Blob::class => "BYTEA",
69
70
            // https://www.postgresql.org/docs/current/datatype-datetime.html
71
            DateTime::class => "TIMESTAMP",
72
73
            default => parent::render_type_name($column)
74
        };
75
    }
76
77
    private function render_column_constraint(Column $column): string
78
    {
79
        $constraint = '';
80
81
        if ($column instanceof Integer && !$column instanceof Boolean && !$column instanceof Serial) {
82
            $constraint .= $column->unsigned ? " UNSIGNED" : '';
83
        }
84
85
        $constraint .= $column->null ? " NULL" : " NOT NULL";
86
        $constraint .= $column->default !== null ? " DEFAULT " . $this->format_default($column->default) : '';
87
        $constraint .= $column->unique ? " UNIQUE" : '';
88
        $constraint .= $column->collate ? " COLLATE $column->collate" : '';
89
90
        // foreign-key-clause goes here
91
92
        return ltrim($constraint);
93
    }
94
95
    private function format_default(string $default): string
96
    {
97
        if (in_array($default, [ DateTime::CURRENT_TIMESTAMP, Date::CURRENT_DATE, Time::CURRENT_TIME ])) {
98
            return "($default)";
99
        }
100
101
        return $default;
102
    }
103
104
    protected function render_table_constraints(Schema $schema): array
105
    {
106
        $constraints = [];
107
108
        //
109
        // PRIMARY KEY
110
        //
111
        $primary = $schema->primary;
112
113
        if (is_array($primary)) {
114
            $primary = implode(', ', $primary);
115
            $constraints[] = "PRIMARY KEY ($primary)";
116
        } elseif (is_string($primary)) {
117
            $constraints[] = "PRIMARY KEY ($primary)";
118
        }
119
120
        //
121
        // UNIQUE
122
        //
123
        foreach ($schema->indexes as $index) {
124
            if (!$index->unique || $index->name) {
125
                continue;
126
            }
127
128
            $indexed_columns = is_array($index->columns)
129
                ? implode(', ', $index->columns)
130
                : $index->columns;
131
            $constraints[] = "UNIQUE ($indexed_columns)";
132
        }
133
134
        return $constraints;
135
    }
136
137
    protected function render_create_index(Schema $schema, string $prefixed_table_name): string
138
    {
139
        $create_index = '';
140
141
        foreach ($schema->indexes as $index) {
142
            $name = $index->name;
143
144
            // Unnamed UNIQUE indexes have been added during render_table_constraints()
145
            if ($index->unique && !$name) {
146
                continue;
147
            }
148
149
            $unique = $index->unique ? 'UNIQUE ' : '';
150
            $columns = $index->columns;
151
            if (!$name) {
152
                $name = is_array($columns) ? implode('_', $columns) : $columns;
153
            }
154
            $columns = $this->render_column_list($columns);
155
            $create_index .= "CREATE {$unique}INDEX $name ON $prefixed_table_name ($columns);\n";
156
        }
157
158
        return rtrim($create_index, "\n");
159
    }
160
161
    /**
162
     * @param string|string[] $columns
163
     */
164
    private function render_column_list(string|array $columns): string
165
    {
166
        return is_array($columns) ? implode(', ', $columns) : $columns;
0 ignored issues
show
introduced by
The condition is_array($columns) is always true.
Loading history...
167
    }
168
169
    protected function render_table_options(): array
170
    {
171
        return [];
172
    }
173
}
174