Passed
Push — main ( 04eeaf...4d8086 )
by Thierry
04:48 queued 03:06
created

Grammar::limit()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 2
c 0
b 0
f 0
nc 4
nop 5
dl 0
loc 4
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\Driver\PgSql\Db;
4
5
use Lagdo\DbAdmin\Driver\Db\Grammar as AbstractGrammar;
6
7
class Grammar extends AbstractGrammar
8
{
9
    /**
10
     * @inheritDoc
11
     */
12
    public function escapeId($idf)
13
    {
14
        return '"' . str_replace('"', '""', $idf) . '"';
15
    }
16
17
    private function constraints(string $table)
18
    {
19
        $constraints = [];
20
        $query = "SELECT conname, consrc FROM pg_catalog.pg_constraint " .
21
            "INNER JOIN pg_catalog.pg_namespace ON pg_constraint.connamespace = pg_namespace.oid " .
22
            "INNER JOIN pg_catalog.pg_class ON pg_constraint.conrelid = pg_class.oid " .
23
            "AND pg_constraint.connamespace = pg_class.relnamespace WHERE pg_constraint.contype = 'c' " .
24
            // "-- handle only CONSTRAINTs here, not TYPES " .
25
            "AND conrelid != 0  AND nspname = current_schema() AND relname = " .
26
            $this->driver->quote($table) . "ORDER BY connamespace, conname";
27
        foreach ($this->driver->rows($query) as $row)
28
        {
29
            $constraints[$row['conname']] = $row['consrc'];
30
        }
31
        return $constraints;
32
    }
33
34
    /**
35
     * @inheritDoc
36
     */
37
    public function sqlForForeignKeys(string $table)
38
    {
39
        $query = "";
40
41
        $status = $this->driver->tableStatus($table);
42
        $fkeys = $this->driver->foreignKeys($table);
43
        ksort($fkeys);
44
45
        foreach ($fkeys as $fkey_name => $fkey) {
46
            $query .= "ALTER TABLE ONLY " . $this->escapeId($status->schema) . "." .
47
                $this->escapeId($status->name) . " ADD CONSTRAINT " . $this->escapeId($fkey_name) .
48
                " {$fkey->definition} " . ($fkey->deferrable ? 'DEFERRABLE' : 'NOT DEFERRABLE') . ";\n";
49
        }
50
51
        return ($query ? "$query\n" : $query);
52
    }
53
54
    /**
55
     * @inheritDoc
56
     */
57
    public function sqlForCreateTable(string $table, bool $autoIncrement, string $style)
58
    {
59
        $clauses = [];
60
        $sequences = [];
61
62
        $status = $this->driver->tableStatus($table);
63
        if ($status !== null && $this->driver->isView($status)) {
64
            $view = $this->driver->view($table);
65
            return rtrim("CREATE VIEW " . $this->escapeId($table) . " AS $view[select]", ";");
66
        }
67
        $fields = $this->driver->fields($table);
68
        $indexes = $this->driver->indexes($table);
69
        ksort($indexes);
70
        $constraints = $this->constraints($table);
71
72
        if (empty($status) || empty($fields)) {
73
            return '';
74
        }
75
76
        $query = "CREATE TABLE " . $this->escapeId($status->schema) . "." .
77
            $this->escapeId($status->name) . " (\n    ";
78
79
        // fields' definitions
80
        foreach ($fields as $field_name => $field) {
81
            $part = $this->escapeId($field->name) . ' ' . $field->fullType .
82
                $this->driver->defaultValue($field) . ($field->attnotnull ? " NOT NULL" : "");
83
            $clauses[] = $part;
84
85
            // sequences for fields
86
            if (preg_match('~nextval\(\'([^\']+)\'\)~', $field->default, $matches)) {
87
                $sequence_name = $matches[1];
88
                $sq = reset($this->driver->rows($this->driver->minVersion(10) ?
89
                    "SELECT *, cache_size AS cache_value FROM pg_sequences " .
90
                    "WHERE schemaname = current_schema() AND sequencename = " .
91
                    $this->driver->quote($sequence_name) : "SELECT * FROM $sequence_name"));
92
                $sequences[] = ($style == "DROP+CREATE" ? "DROP SEQUENCE IF EXISTS $sequence_name;\n" : "") .
93
                    "CREATE SEQUENCE $sequence_name INCREMENT $sq[increment_by] MINVALUE $sq[min_value] MAXVALUE $sq[max_value]" .
94
                    ($autoIncrement && $sq['last_value'] ? " START $sq[last_value]" : "") . " CACHE $sq[cache_value];";
95
            }
96
        }
97
98
        // adding sequences before table definition
99
        if (!empty($sequences)) {
100
            $query = implode("\n\n", $sequences) . "\n\n$query";
101
        }
102
103
        // primary + unique keys
104
        foreach ($indexes as $index_name => $index) {
105
            switch ($index->type) {
106
                case 'UNIQUE':
107
                    $clauses[] = "CONSTRAINT " . $this->escapeId($index_name) .
108
                        " UNIQUE (" . implode(', ', array_map(function ($column) {
109
                            return $this->escapeId($column);
110
                        }, $index->columns)) . ")";
111
                    break;
112
                case 'PRIMARY':
113
                    $clauses[] = "CONSTRAINT " . $this->escapeId($index_name) .
114
                        " PRIMARY KEY (" . implode(', ', array_map(function ($column) {
115
                            return $this->escapeId($column);
116
                        }, $index->columns)) . ")";
117
                    break;
118
            }
119
        }
120
121
        foreach ($constraints as $conname => $consrc) {
122
            $clauses[] = "CONSTRAINT " . $this->escapeId($conname) . " CHECK $consrc";
123
        }
124
125
        $query .= implode(",\n    ", $clauses) . "\n) WITH (oids = " . ($status->oid ? 'true' : 'false') . ");";
126
127
        // "basic" indexes after table definition
128
        foreach ($indexes as $index_name => $index) {
129
            if ($index->type == 'INDEX') {
130
                $columns = [];
131
                foreach ($index->columns as $key => $val) {
132
                    $columns[] = $this->escapeId($val) . ($index->descs[$key] ? " DESC" : "");
133
                }
134
                $query .= "\n\nCREATE INDEX " . $this->escapeId($index_name) . " ON " .
135
                    $this->escapeId($status->schema) . "." . $this->escapeId($status->name) .
136
                    " USING btree (" . implode(', ', $columns) . ");";
137
            }
138
        }
139
140
        // coments for table & fields
141
        if ($status->comment) {
142
            $query .= "\n\nCOMMENT ON TABLE " . $this->escapeId($status->schema) . "." .
143
                $this->escapeId($status->name) . " IS " . $this->driver->quote($status->comment) . ";";
144
        }
145
146
        foreach ($fields as $field_name => $field) {
147
            if ($field->comment) {
148
                $query .= "\n\nCOMMENT ON COLUMN " . $this->escapeId($status->schema) . "." .
149
                    $this->escapeId($status->name) . "." . $this->escapeId($field_name) .
150
                    " IS " . $this->driver->quote($field->comment) . ";";
151
            }
152
        }
153
154
        return rtrim($query, ';');
155
    }
156
157
    /**
158
     * @inheritDoc
159
     */
160
    public function sqlForTruncateTable(string $table)
161
    {
162
        return "TRUNCATE " . $this->table($table);
163
    }
164
165
    /**
166
     * @inheritDoc
167
     */
168
    public function sqlForCreateTrigger(string $table)
169
    {
170
        $status = $this->driver->tableStatus($table);
171
        $query = "";
172
        foreach ($this->driver->triggers($table) as $trg_id => $trg) {
173
            $trigger = $this->driver->trigger($trg_id, $status->name);
174
            $query .= "\nCREATE TRIGGER " . $this->escapeId($trigger['Trigger']) .
175
                " $trigger[Timing] $trigger[Events] ON " . $this->escapeId($status->schema) . "." .
176
                $this->escapeId($status->name) . " $trigger[Type] $trigger[Statement];;\n";
177
        }
178
        return $query;
179
    }
180
181
182
    /**
183
     * @inheritDoc
184
     */
185
    public function sqlForUseDatabase(string $database)
186
    {
187
        return "\connect " . $this->escapeId($database);
188
    }
189
}
190