1 | <?php |
||||||||
2 | |||||||||
3 | /** |
||||||||
4 | * PHPPgAdmin 6.1.3 |
||||||||
5 | */ |
||||||||
6 | |||||||||
7 | namespace PHPPgAdmin\Database\Traits; |
||||||||
8 | |||||||||
9 | /** |
||||||||
10 | * Common trait for tables manipulation. |
||||||||
11 | */ |
||||||||
12 | trait TableTrait |
||||||||
13 | { |
||||||||
14 | use \PHPPgAdmin\Database\Traits\ColumnTrait; |
||||||||
15 | use \PHPPgAdmin\Database\Traits\RowTrait; |
||||||||
16 | use \PHPPgAdmin\Database\Traits\TriggerTrait; |
||||||||
17 | |||||||||
18 | /** |
||||||||
19 | * Return all tables in current database excluding schemas 'pg_catalog', 'information_schema' and 'pg_toast'. |
||||||||
20 | * |
||||||||
21 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
22 | */ |
||||||||
23 | public function getAllTables() |
||||||||
24 | { |
||||||||
25 | $sql = "SELECT |
||||||||
26 | schemaname AS nspname, |
||||||||
27 | tablename AS relname, |
||||||||
28 | tableowner AS relowner |
||||||||
29 | FROM pg_catalog.pg_tables |
||||||||
30 | WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') |
||||||||
31 | ORDER BY schemaname, tablename"; |
||||||||
32 | |||||||||
33 | return $this->selectSet($sql); |
||||||||
34 | } |
||||||||
35 | |||||||||
36 | /** |
||||||||
37 | * Return all tables in current database (and schema). |
||||||||
38 | * |
||||||||
39 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
40 | */ |
||||||||
41 | public function getTables() |
||||||||
42 | { |
||||||||
43 | $c_schema = $this->_schema; |
||||||||
44 | $this->clean($c_schema); |
||||||||
45 | |||||||||
46 | $sql = " |
||||||||
47 | SELECT c.relname, |
||||||||
48 | pg_catalog.pg_get_userbyid(c.relowner) AS relowner, |
||||||||
49 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment, |
||||||||
50 | reltuples::bigint as reltuples, |
||||||||
51 | pt.spcname as tablespace, "; |
||||||||
52 | |||||||||
53 | /* |
||||||||
54 | * Either display_sizes is true for tables and schemas, |
||||||||
55 | * or we must check if said config is an associative array |
||||||||
56 | */ |
||||||||
57 | if ($this->conf['display_sizes']['tables']) { |
||||||||
58 | $sql .= ' pg_size_pretty(pg_total_relation_size(c.oid)) as table_size '; |
||||||||
59 | } else { |
||||||||
60 | $sql .= " 'N/A' as table_size "; |
||||||||
61 | } |
||||||||
62 | |||||||||
63 | $sql .= " FROM pg_catalog.pg_class c |
||||||||
64 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace |
||||||||
65 | LEFT JOIN pg_catalog.pg_tablespace pt ON pt.oid=c.reltablespace |
||||||||
66 | WHERE c.relkind = 'r' |
||||||||
67 | AND nspname='{$c_schema}' |
||||||||
68 | ORDER BY c.relname"; |
||||||||
69 | |||||||||
70 | return $this->selectSet($sql); |
||||||||
71 | } |
||||||||
72 | |||||||||
73 | /** |
||||||||
74 | * Finds the names and schemas of parent tables (in order). |
||||||||
75 | * |
||||||||
76 | * @param string $table The table to find the parents for |
||||||||
77 | * |
||||||||
78 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
79 | */ |
||||||||
80 | public function getTableParents($table) |
||||||||
81 | { |
||||||||
82 | $c_schema = $this->_schema; |
||||||||
83 | $this->clean($c_schema); |
||||||||
84 | $this->clean($table); |
||||||||
85 | |||||||||
86 | $sql = " |
||||||||
87 | SELECT |
||||||||
88 | pn.nspname, relname |
||||||||
89 | FROM |
||||||||
90 | pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn |
||||||||
91 | WHERE |
||||||||
92 | pc.oid=pi.inhparent |
||||||||
93 | AND pc.relnamespace=pn.oid |
||||||||
94 | AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
95 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}')) |
||||||||
96 | ORDER BY |
||||||||
97 | pi.inhseqno |
||||||||
98 | "; |
||||||||
99 | |||||||||
100 | return $this->selectSet($sql); |
||||||||
101 | } |
||||||||
102 | |||||||||
103 | /** |
||||||||
104 | * Finds the names and schemas of child tables. |
||||||||
105 | * |
||||||||
106 | * @param string $table The table to find the children for |
||||||||
107 | * |
||||||||
108 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
109 | */ |
||||||||
110 | public function getTableChildren($table) |
||||||||
111 | { |
||||||||
112 | $c_schema = $this->_schema; |
||||||||
113 | $this->clean($c_schema); |
||||||||
114 | $this->clean($table); |
||||||||
115 | |||||||||
116 | $sql = " |
||||||||
117 | SELECT |
||||||||
118 | pn.nspname, relname |
||||||||
119 | FROM |
||||||||
120 | pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn |
||||||||
121 | WHERE |
||||||||
122 | pc.oid=pi.inhrelid |
||||||||
123 | AND pc.relnamespace=pn.oid |
||||||||
124 | AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
125 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}')) |
||||||||
126 | "; |
||||||||
127 | |||||||||
128 | return $this->selectSet($sql); |
||||||||
129 | } |
||||||||
130 | |||||||||
131 | /** |
||||||||
132 | * Returns the SQL definition for the table. |
||||||||
133 | * MUST be run within a transaction. |
||||||||
134 | * |
||||||||
135 | * @param string $table The table to define |
||||||||
136 | * @param string $cleanprefix set to '-- ' to avoid issuing DROP statement |
||||||||
137 | */ |
||||||||
138 | public function getTableDefPrefix($table, $cleanprefix = ''): ?string |
||||||||
139 | { |
||||||||
140 | // Fetch table |
||||||||
141 | $t = $this->getTable($table); |
||||||||
142 | |||||||||
143 | if (!\is_object($t) || 1 !== $t->recordCount()) { |
||||||||
144 | $this->rollbackTransaction(); |
||||||||
145 | |||||||||
146 | return null; |
||||||||
147 | } |
||||||||
148 | $this->fieldClean($t->fields['relname']); |
||||||||
149 | $this->fieldClean($t->fields['nspname']); |
||||||||
150 | |||||||||
151 | // Fetch attributes |
||||||||
152 | $atts = $this->getTableAttributes($table); |
||||||||
153 | |||||||||
154 | if (!\is_object($atts)) { |
||||||||
155 | $this->rollbackTransaction(); |
||||||||
156 | |||||||||
157 | return null; |
||||||||
158 | } |
||||||||
159 | |||||||||
160 | // Fetch constraints |
||||||||
161 | $cons = $this->getConstraints($table); |
||||||||
162 | |||||||||
163 | if (!\is_object($cons)) { |
||||||||
164 | $this->rollbackTransaction(); |
||||||||
165 | |||||||||
166 | return null; |
||||||||
167 | } |
||||||||
168 | |||||||||
169 | // Output a reconnect command to create the table as the correct user |
||||||||
170 | $sql = "-- PHPPgAdmin\n" . $this->getChangeUserSQL($t->fields['relowner']) . "\n\n"; |
||||||||
171 | |||||||||
172 | $sql = $this->_dumpCreate($t, $sql, $cleanprefix); |
||||||||
173 | |||||||||
174 | // Output all table columns |
||||||||
175 | $col_comments_sql = ''; // Accumulate comments on columns |
||||||||
176 | $num = $atts->recordCount() + $cons->recordCount(); |
||||||||
177 | $i = 1; |
||||||||
178 | |||||||||
179 | $sql = $this->_dumpSerials($atts, $t, $sql, $col_comments_sql, $i, $num); |
||||||||
180 | |||||||||
181 | $consOutput = $this->_dumpConstraints($cons, $table, $sql, $i, $num); |
||||||||
182 | |||||||||
183 | if (null === $consOutput) { |
||||||||
184 | return null; |
||||||||
185 | } |
||||||||
186 | $sql = $consOutput; |
||||||||
187 | |||||||||
188 | $sql .= ')'; |
||||||||
189 | |||||||||
190 | // Handle WITHOUT OIDS |
||||||||
191 | if ($this->hasObjectID($table)) { |
||||||||
192 | $sql .= ' WITH OIDS'; |
||||||||
193 | } else { |
||||||||
194 | $sql .= ' WITHOUT OIDS'; |
||||||||
195 | } |
||||||||
196 | |||||||||
197 | $sql .= ";\n"; |
||||||||
198 | |||||||||
199 | $colStorage = $this->_dumpColStats($atts, $t, $sql); |
||||||||
200 | |||||||||
201 | if (null === $colStorage) { |
||||||||
202 | return null; |
||||||||
203 | } |
||||||||
204 | $sql = $colStorage; |
||||||||
205 | |||||||||
206 | // Comment |
||||||||
207 | if (null !== $t->fields['relcomment']) { |
||||||||
208 | $this->clean($t->fields['relcomment']); |
||||||||
209 | $sql .= "\n-- Comment\n\n"; |
||||||||
210 | $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n"; |
||||||||
211 | } |
||||||||
212 | |||||||||
213 | // Add comments on columns, if any |
||||||||
214 | if ('' !== $col_comments_sql) { |
||||||||
215 | $sql .= $col_comments_sql; |
||||||||
216 | } |
||||||||
217 | |||||||||
218 | // Privileges |
||||||||
219 | $privs = $this->getPrivileges($table, 'table'); |
||||||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||||
220 | |||||||||
221 | if (!\is_array($privs)) { |
||||||||
222 | $this->rollbackTransaction(); |
||||||||
223 | |||||||||
224 | return null; |
||||||||
225 | } |
||||||||
226 | |||||||||
227 | $privsOutput = $this->_dumpPrivileges($privs, $t, $sql); |
||||||||
0 ignored issues
–
show
$privs of type array is incompatible with the type PHPPgAdmin\ADORecordSet expected by parameter $privs of PHPPgAdmin\Database\Trai...rait::_dumpPrivileges() .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||
228 | |||||||||
229 | if (null === $privsOutput) { |
||||||||
230 | return null; |
||||||||
231 | } |
||||||||
232 | $sql = $privsOutput; |
||||||||
233 | |||||||||
234 | // Add a newline to separate data that follows (if any) |
||||||||
235 | $sql .= "\n"; |
||||||||
236 | |||||||||
237 | return $sql; |
||||||||
238 | } |
||||||||
239 | |||||||||
240 | /** |
||||||||
241 | * Returns table information. |
||||||||
242 | * |
||||||||
243 | * @param string $table The name of the table |
||||||||
244 | * |
||||||||
245 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
246 | */ |
||||||||
247 | public function getTable($table) |
||||||||
248 | { |
||||||||
249 | $c_schema = $this->_schema; |
||||||||
250 | $this->clean($c_schema); |
||||||||
251 | $this->clean($table); |
||||||||
252 | |||||||||
253 | $sql = ' |
||||||||
254 | SELECT |
||||||||
255 | c.relname, n.nspname, '; |
||||||||
256 | |||||||||
257 | $sql .= ($this->hasRoles() ? ' coalesce(u.usename,r.rolname) ' : ' u.usename') . " AS relowner, |
||||||||
258 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment, |
||||||||
259 | pt.spcname AS tablespace |
||||||||
260 | FROM pg_catalog.pg_class c |
||||||||
261 | LEFT JOIN pg_catalog.pg_tablespace pt ON pt.oid=c.reltablespace |
||||||||
262 | LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner |
||||||||
263 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace "; |
||||||||
264 | |||||||||
265 | $sql .= ($this->hasRoles() ? ' LEFT JOIN pg_catalog.pg_roles r ON c.relowner = r.oid ' : '') . |
||||||||
266 | " WHERE c.relkind = 'r' |
||||||||
267 | AND n.nspname = '{$c_schema}' |
||||||||
268 | AND n.oid = c.relnamespace |
||||||||
269 | AND c.relname = '{$table}'"; |
||||||||
270 | |||||||||
271 | return $this->selectSet($sql); |
||||||||
272 | } |
||||||||
273 | |||||||||
274 | /** |
||||||||
275 | * Retrieve the attribute definition of a table. |
||||||||
276 | * |
||||||||
277 | * @param string $table The name of the table |
||||||||
278 | * @param string $field (optional) The name of a field to return |
||||||||
279 | * |
||||||||
280 | * @return \PHPPgAdmin\ADORecordSet All attributes in order |
||||||||
281 | */ |
||||||||
282 | public function getTableAttributes($table, $field = '') |
||||||||
283 | { |
||||||||
284 | $c_schema = $this->_schema; |
||||||||
285 | $this->clean($c_schema); |
||||||||
286 | $this->clean($table); |
||||||||
287 | |||||||||
288 | if ('' === $field) { |
||||||||
289 | // This query is made much more complex by the addition of the 'attisserial' field. |
||||||||
290 | // The subquery to get that field checks to see if there is an internally dependent |
||||||||
291 | // sequence on the field. |
||||||||
292 | return $this->_getTableAttributesAll($table, $c_schema); |
||||||||
0 ignored issues
–
show
|
|||||||||
293 | } |
||||||||
294 | $this->clean($field); |
||||||||
295 | |||||||||
296 | return $this->_getTableAttribute($table, $c_schema, $field); |
||||||||
0 ignored issues
–
show
|
|||||||||
297 | } |
||||||||
298 | |||||||||
299 | /** |
||||||||
300 | * Returns a list of all constraints on a table. |
||||||||
301 | * |
||||||||
302 | * @param string $table The table to find rules for |
||||||||
303 | * |
||||||||
304 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
305 | */ |
||||||||
306 | public function getConstraints($table) |
||||||||
307 | { |
||||||||
308 | $c_schema = $this->_schema; |
||||||||
309 | $this->clean($c_schema); |
||||||||
310 | $this->clean($table); |
||||||||
311 | |||||||||
312 | // This SQL is greatly complicated by the need to retrieve |
||||||||
313 | // index clustering information for primary and unique constraints |
||||||||
314 | $sql = "SELECT |
||||||||
315 | pc.conname, |
||||||||
316 | pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc, |
||||||||
317 | pc.contype, |
||||||||
318 | CASE WHEN pc.contype='u' OR pc.contype='p' THEN ( |
||||||||
319 | SELECT |
||||||||
320 | indisclustered |
||||||||
321 | FROM |
||||||||
322 | pg_catalog.pg_depend pd, |
||||||||
323 | pg_catalog.pg_class pl, |
||||||||
324 | pg_catalog.pg_index pi |
||||||||
325 | WHERE |
||||||||
326 | pd.refclassid=pc.tableoid |
||||||||
327 | AND pd.refobjid=pc.oid |
||||||||
328 | AND pd.objid=pl.oid |
||||||||
329 | AND pl.oid=pi.indexrelid |
||||||||
330 | ) ELSE |
||||||||
331 | NULL |
||||||||
332 | END AS indisclustered |
||||||||
333 | FROM |
||||||||
334 | pg_catalog.pg_constraint pc |
||||||||
335 | WHERE |
||||||||
336 | pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
337 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||||||||
338 | WHERE nspname='{$c_schema}')) |
||||||||
339 | ORDER BY |
||||||||
340 | 1 |
||||||||
341 | "; |
||||||||
342 | |||||||||
343 | return $this->selectSet($sql); |
||||||||
344 | } |
||||||||
345 | |||||||||
346 | /** |
||||||||
347 | * Checks to see whether or not a table has a unique id column. |
||||||||
348 | * |
||||||||
349 | * @param string $table The table name |
||||||||
350 | * |
||||||||
351 | * @return bool true if it has a unique id, false otherwise |
||||||||
352 | */ |
||||||||
353 | public function hasObjectID($table) |
||||||||
354 | { |
||||||||
355 | $c_schema = $this->_schema; |
||||||||
356 | $this->clean($c_schema); |
||||||||
357 | $this->clean($table); |
||||||||
358 | |||||||||
359 | $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
360 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')"; |
||||||||
361 | |||||||||
362 | $rs = $this->selectSet($sql); |
||||||||
363 | |||||||||
364 | if (1 !== $rs->recordCount()) { |
||||||||
365 | return false; |
||||||||
366 | } |
||||||||
367 | |||||||||
368 | $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']); |
||||||||
369 | |||||||||
370 | return $rs->fields['relhasoids']; |
||||||||
371 | } |
||||||||
372 | |||||||||
373 | /** |
||||||||
374 | * Returns extra table definition information that is most usefully |
||||||||
375 | * dumped after the table contents for speed and efficiency reasons. |
||||||||
376 | * |
||||||||
377 | * @param string $table The table to define |
||||||||
378 | */ |
||||||||
379 | public function getTableDefSuffix($table): ?string |
||||||||
380 | { |
||||||||
381 | $sql = ''; |
||||||||
382 | |||||||||
383 | // Indexes |
||||||||
384 | $indexes = $this->getIndexes($table); |
||||||||
385 | |||||||||
386 | if (!\is_object($indexes)) { |
||||||||
387 | $this->rollbackTransaction(); |
||||||||
388 | |||||||||
389 | return null; |
||||||||
390 | } |
||||||||
391 | |||||||||
392 | if (0 < $indexes->recordCount()) { |
||||||||
393 | $sql .= "\n-- Indexes\n\n"; |
||||||||
394 | |||||||||
395 | while (!$indexes->EOF) { |
||||||||
396 | $sql .= $indexes->fields['inddef'] . ";\n"; |
||||||||
397 | |||||||||
398 | $indexes->moveNext(); |
||||||||
399 | } |
||||||||
400 | } |
||||||||
401 | |||||||||
402 | // Triggers |
||||||||
403 | $triggers = $this->getTriggers($table); |
||||||||
404 | |||||||||
405 | if (!\is_object($triggers)) { |
||||||||
406 | $this->rollbackTransaction(); |
||||||||
407 | |||||||||
408 | return null; |
||||||||
409 | } |
||||||||
410 | |||||||||
411 | if (0 < $triggers->recordCount()) { |
||||||||
412 | $sql .= "\n-- Triggers\n\n"; |
||||||||
413 | |||||||||
414 | while (!$triggers->EOF) { |
||||||||
415 | $sql .= $triggers->fields['tgdef']; |
||||||||
416 | $sql .= ";\n"; |
||||||||
417 | |||||||||
418 | $triggers->moveNext(); |
||||||||
419 | } |
||||||||
420 | } |
||||||||
421 | |||||||||
422 | // Rules |
||||||||
423 | $rules = $this->getRules($table); |
||||||||
424 | |||||||||
425 | if (!\is_object($rules)) { |
||||||||
426 | $this->rollbackTransaction(); |
||||||||
427 | |||||||||
428 | return null; |
||||||||
429 | } |
||||||||
430 | |||||||||
431 | if (0 < $rules->recordCount()) { |
||||||||
432 | $sql .= "\n-- Rules\n\n"; |
||||||||
433 | |||||||||
434 | while (!$rules->EOF) { |
||||||||
435 | $sql .= $rules->fields['definition'] . "\n"; |
||||||||
436 | |||||||||
437 | $rules->moveNext(); |
||||||||
438 | } |
||||||||
439 | } |
||||||||
440 | |||||||||
441 | return $sql; |
||||||||
442 | } |
||||||||
443 | |||||||||
444 | /** |
||||||||
445 | * Grabs a list of indexes for a table. |
||||||||
446 | * |
||||||||
447 | * @param string $table The name of a table whose indexes to retrieve |
||||||||
448 | * @param bool $unique Only get unique/pk indexes |
||||||||
449 | * |
||||||||
450 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
451 | */ |
||||||||
452 | public function getIndexes($table = '', $unique = false) |
||||||||
453 | { |
||||||||
454 | $this->clean($table); |
||||||||
455 | |||||||||
456 | $sql = " |
||||||||
457 | SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered, |
||||||||
458 | pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef |
||||||||
459 | FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i |
||||||||
460 | WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid) |
||||||||
461 | AND c.oid = i.indrelid AND i.indexrelid = c2.oid |
||||||||
462 | "; |
||||||||
463 | |||||||||
464 | if ($unique) { |
||||||||
465 | $sql .= ' AND i.indisunique '; |
||||||||
466 | } |
||||||||
467 | |||||||||
468 | $sql .= ' ORDER BY c2.relname'; |
||||||||
469 | |||||||||
470 | return $this->selectSet($sql); |
||||||||
471 | } |
||||||||
472 | |||||||||
473 | /** |
||||||||
474 | * Grabs a list of triggers on a table. |
||||||||
475 | * |
||||||||
476 | * @param string $table The name of a table whose triggers to retrieve |
||||||||
477 | * |
||||||||
478 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
479 | */ |
||||||||
480 | public function getTriggers($table = '') |
||||||||
481 | { |
||||||||
482 | $c_schema = $this->_schema; |
||||||||
483 | $this->clean($c_schema); |
||||||||
484 | $this->clean($table); |
||||||||
485 | |||||||||
486 | $sql = "SELECT |
||||||||
487 | t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef, |
||||||||
488 | CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid, |
||||||||
489 | p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto, |
||||||||
490 | ns.nspname AS pronamespace |
||||||||
491 | FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns |
||||||||
492 | WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
493 | AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')) |
||||||||
494 | AND ( tgconstraint = 0 OR NOT EXISTS |
||||||||
495 | (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c |
||||||||
496 | ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) |
||||||||
497 | WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f')) |
||||||||
498 | AND p.oid=t.tgfoid |
||||||||
499 | AND p.pronamespace = ns.oid"; |
||||||||
500 | |||||||||
501 | return $this->selectSet($sql); |
||||||||
502 | } |
||||||||
503 | |||||||||
504 | /** |
||||||||
505 | * Returns a list of all rules on a table OR view. |
||||||||
506 | * |
||||||||
507 | * @param string $table The table to find rules for |
||||||||
508 | * |
||||||||
509 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
510 | */ |
||||||||
511 | public function getRules($table) |
||||||||
512 | { |
||||||||
513 | $c_schema = $this->_schema; |
||||||||
514 | $this->clean($c_schema); |
||||||||
515 | $this->clean($table); |
||||||||
516 | |||||||||
517 | $sql = " |
||||||||
518 | SELECT * |
||||||||
519 | FROM pg_catalog.pg_rules |
||||||||
520 | WHERE |
||||||||
521 | schemaname='{$c_schema}' AND tablename='{$table}' |
||||||||
522 | ORDER BY rulename |
||||||||
523 | "; |
||||||||
524 | |||||||||
525 | return $this->selectSet($sql); |
||||||||
526 | } |
||||||||
527 | |||||||||
528 | /** |
||||||||
529 | * Creates a new table in the database. |
||||||||
530 | * |
||||||||
531 | * @param string $name The name of the table |
||||||||
532 | * @param int $fields The number of fields |
||||||||
533 | * @param array $field An array of field names |
||||||||
534 | * @param array $type An array of field types |
||||||||
535 | * @param array $array An array of '' or '[]' for each type if it's an array or not |
||||||||
536 | * @param array $length An array of field lengths |
||||||||
537 | * @param array $notnull An array of not null |
||||||||
538 | * @param array $default An array of default values |
||||||||
539 | * @param bool $withoutoids True if WITHOUT OIDS, false otherwise |
||||||||
540 | * @param array $colcomment An array of comments |
||||||||
541 | * @param string $tblcomment the comment for the table |
||||||||
542 | * @param string $tablespace The tablespace name ('' means none/default) |
||||||||
543 | * @param array $uniquekey An Array indicating the fields that are unique (those indexes that are set) |
||||||||
544 | * @param array $primarykey An Array indicating the field used for the primarykey (those indexes that are set) |
||||||||
545 | * |
||||||||
546 | * @return bool|int 0 success |
||||||||
547 | */ |
||||||||
548 | public function createTable( |
||||||||
549 | $name, |
||||||||
550 | $fields, |
||||||||
551 | $field, |
||||||||
552 | $type, |
||||||||
553 | $array, |
||||||||
554 | $length, |
||||||||
555 | $notnull, |
||||||||
556 | $default, |
||||||||
557 | $withoutoids, |
||||||||
558 | $colcomment, |
||||||||
559 | $tblcomment, |
||||||||
560 | $tablespace, |
||||||||
561 | $uniquekey, |
||||||||
562 | $primarykey |
||||||||
563 | ) { |
||||||||
564 | $f_schema = $this->_schema; |
||||||||
565 | $this->fieldClean($f_schema); |
||||||||
566 | $this->fieldClean($name); |
||||||||
567 | |||||||||
568 | $status = $this->beginTransaction(); |
||||||||
569 | |||||||||
570 | if (0 !== $status) { |
||||||||
571 | return -1; |
||||||||
572 | } |
||||||||
573 | |||||||||
574 | $found = false; |
||||||||
575 | $first = true; |
||||||||
576 | $comment_sql = ''; //Accumulate comments for the columns |
||||||||
577 | $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" ("; |
||||||||
578 | |||||||||
579 | for ($i = 0; $i < $fields; ++$i) { |
||||||||
580 | $this->fieldClean($field[$i]); |
||||||||
581 | $this->clean($type[$i]); |
||||||||
582 | $this->clean($length[$i]); |
||||||||
583 | $this->clean($colcomment[$i]); |
||||||||
584 | |||||||||
585 | // Skip blank columns - for user convenience |
||||||||
586 | if ('' === $field[$i] || '' === $type[$i]) { |
||||||||
587 | continue; |
||||||||
588 | } |
||||||||
589 | |||||||||
590 | // If not the first column, add a comma |
||||||||
591 | if (!$first) { |
||||||||
592 | $sql .= ', '; |
||||||||
593 | } else { |
||||||||
594 | $first = false; |
||||||||
595 | } |
||||||||
596 | |||||||||
597 | switch ($type[$i]) { |
||||||||
598 | // Have to account for weird placing of length for with/without |
||||||||
599 | // time zone types |
||||||||
600 | case 'timestamp with time zone': |
||||||||
601 | case 'timestamp without time zone': |
||||||||
602 | $qual = \mb_substr($type[$i], 9); |
||||||||
603 | $sql .= "\"{$field[$i]}\" timestamp"; |
||||||||
604 | |||||||||
605 | if ('' !== $length[$i] ?? '') { |
||||||||
606 | $sql .= "({$length[$i]})"; |
||||||||
607 | } |
||||||||
608 | |||||||||
609 | $sql .= $qual; |
||||||||
610 | |||||||||
611 | break; |
||||||||
612 | case 'time with time zone': |
||||||||
613 | case 'time without time zone': |
||||||||
614 | $qual = \mb_substr($type[$i], 4); |
||||||||
615 | $sql .= "\"{$field[$i]}\" time"; |
||||||||
616 | |||||||||
617 | if ('' !== $length[$i]) { |
||||||||
618 | $sql .= "({$length[$i]})"; |
||||||||
619 | } |
||||||||
620 | |||||||||
621 | $sql .= $qual; |
||||||||
622 | |||||||||
623 | break; |
||||||||
624 | |||||||||
625 | default: |
||||||||
626 | $length[$i] = $length[$i] ?? null; |
||||||||
627 | $sql .= "\"{$field[$i]}\" {$type[$i]}"; |
||||||||
628 | |||||||||
629 | if ('' !== $length[$i] && null !== $length[$i]) { |
||||||||
630 | $sql .= "({$length[$i]})"; |
||||||||
631 | } |
||||||||
632 | } |
||||||||
633 | // Add array qualifier if necessary |
||||||||
634 | if ('[]' === $array[$i]) { |
||||||||
635 | $sql .= '[]'; |
||||||||
636 | } |
||||||||
637 | |||||||||
638 | // Add other qualifiers |
||||||||
639 | if (!isset($primarykey[$i])) { |
||||||||
640 | if (isset($uniquekey[$i])) { |
||||||||
641 | $sql .= ' UNIQUE'; |
||||||||
642 | } |
||||||||
643 | |||||||||
644 | if (isset($notnull[$i])) { |
||||||||
645 | $sql .= ' NOT NULL'; |
||||||||
646 | } |
||||||||
647 | } |
||||||||
648 | |||||||||
649 | if ('' !== $default[$i]) { |
||||||||
650 | $sql .= " DEFAULT {$default[$i]}"; |
||||||||
651 | } |
||||||||
652 | |||||||||
653 | if ('' !== $colcomment[$i]) { |
||||||||
654 | $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n"; |
||||||||
655 | } |
||||||||
656 | |||||||||
657 | $found = true; |
||||||||
658 | } |
||||||||
659 | |||||||||
660 | if (!$found) { |
||||||||
0 ignored issues
–
show
|
|||||||||
661 | return -1; |
||||||||
662 | } |
||||||||
663 | |||||||||
664 | // PRIMARY KEY |
||||||||
665 | $primarykeycolumns = []; |
||||||||
666 | |||||||||
667 | for ($i = 0; $i < $fields; ++$i) { |
||||||||
668 | if (isset($primarykey[$i])) { |
||||||||
669 | $primarykeycolumns[] = "\"{$field[$i]}\""; |
||||||||
670 | } |
||||||||
671 | } |
||||||||
672 | |||||||||
673 | if (0 < \count($primarykeycolumns)) { |
||||||||
674 | $sql .= ', PRIMARY KEY (' . \implode(', ', $primarykeycolumns) . ')'; |
||||||||
675 | } |
||||||||
676 | |||||||||
677 | $sql .= ')'; |
||||||||
678 | |||||||||
679 | // WITHOUT OIDS |
||||||||
680 | if ($withoutoids) { |
||||||||
681 | $sql .= ' WITHOUT OIDS'; |
||||||||
682 | } else { |
||||||||
683 | $sql .= ' WITH OIDS'; |
||||||||
684 | } |
||||||||
685 | |||||||||
686 | // Tablespace |
||||||||
687 | if ($this->hasTablespaces() && '' !== $tablespace) { |
||||||||
688 | $this->fieldClean($tablespace); |
||||||||
689 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||||||||
690 | } |
||||||||
691 | |||||||||
692 | $status = $this->execute($sql); |
||||||||
693 | |||||||||
694 | if ($status) { |
||||||||
695 | $this->rollbackTransaction(); |
||||||||
696 | |||||||||
697 | return -1; |
||||||||
698 | } |
||||||||
699 | |||||||||
700 | if ('' !== $tblcomment) { |
||||||||
701 | $status = $this->setComment('TABLE', '', $name, $tblcomment, true); |
||||||||
702 | |||||||||
703 | if ($status) { |
||||||||
704 | $this->rollbackTransaction(); |
||||||||
705 | |||||||||
706 | return -1; |
||||||||
707 | } |
||||||||
708 | } |
||||||||
709 | |||||||||
710 | if ('' !== $comment_sql) { |
||||||||
711 | $status = $this->execute($comment_sql); |
||||||||
712 | |||||||||
713 | if ($status) { |
||||||||
714 | $this->rollbackTransaction(); |
||||||||
715 | |||||||||
716 | return -1; |
||||||||
717 | } |
||||||||
718 | } |
||||||||
719 | |||||||||
720 | return $this->endTransaction(); |
||||||||
721 | } |
||||||||
722 | |||||||||
723 | /** |
||||||||
724 | * Creates a new table in the database copying attribs and other properties from another table. |
||||||||
725 | * |
||||||||
726 | * @param string $name The name of the table |
||||||||
727 | * @param array $like an array giving the schema ans the name of the table from which attribs are copying |
||||||||
728 | * from: array( |
||||||||
729 | * 'table' => table name, |
||||||||
730 | * 'schema' => the schema name, |
||||||||
731 | * ) |
||||||||
732 | * @param bool $defaults if true, copy the defaults values as well |
||||||||
733 | * @param bool $constraints if true, copy the constraints as well (CHECK on table & attr) |
||||||||
734 | * @param bool $idx |
||||||||
735 | * @param string $tablespace The tablespace name ('' means none/default) |
||||||||
736 | * |
||||||||
737 | * @return bool|int |
||||||||
738 | */ |
||||||||
739 | public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '') |
||||||||
740 | { |
||||||||
741 | $f_schema = $this->_schema; |
||||||||
742 | $this->fieldClean($f_schema); |
||||||||
743 | $this->fieldClean($name); |
||||||||
744 | $this->fieldClean($like['schema']); |
||||||||
745 | $this->fieldClean($like['table']); |
||||||||
746 | $like = "\"{$like['schema']}\".\"{$like['table']}\""; |
||||||||
747 | |||||||||
748 | $status = $this->beginTransaction(); |
||||||||
749 | |||||||||
750 | if (0 !== $status) { |
||||||||
751 | return -1; |
||||||||
752 | } |
||||||||
753 | |||||||||
754 | $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}"; |
||||||||
755 | |||||||||
756 | if ($defaults) { |
||||||||
757 | $sql .= ' INCLUDING DEFAULTS'; |
||||||||
758 | } |
||||||||
759 | |||||||||
760 | if ($this->hasCreateTableLikeWithConstraints() && $constraints) { |
||||||||
761 | $sql .= ' INCLUDING CONSTRAINTS'; |
||||||||
762 | } |
||||||||
763 | |||||||||
764 | if ($this->hasCreateTableLikeWithIndexes() && $idx) { |
||||||||
765 | $sql .= ' INCLUDING INDEXES'; |
||||||||
766 | } |
||||||||
767 | |||||||||
768 | $sql .= ')'; |
||||||||
769 | |||||||||
770 | if ($this->hasTablespaces() && '' !== $tablespace) { |
||||||||
771 | $this->fieldClean($tablespace); |
||||||||
772 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||||||||
773 | } |
||||||||
774 | |||||||||
775 | $status = $this->execute($sql); |
||||||||
776 | |||||||||
777 | if ($status) { |
||||||||
778 | $this->rollbackTransaction(); |
||||||||
779 | |||||||||
780 | return -1; |
||||||||
781 | } |
||||||||
782 | |||||||||
783 | return $this->endTransaction(); |
||||||||
784 | } |
||||||||
785 | |||||||||
786 | /** |
||||||||
787 | * Alter table properties. |
||||||||
788 | * |
||||||||
789 | * @param string $table The name of the table |
||||||||
790 | * @param string $name The new name for the table |
||||||||
791 | * @param string $owner The new owner for the table |
||||||||
792 | * @param string $schema The new schema for the table |
||||||||
793 | * @param string $comment The comment on the table |
||||||||
794 | * @param string $tablespace The new tablespace for the table ('' means leave as is) |
||||||||
795 | * @param bool $with_oids If set to FALSE, will drop oids column |
||||||||
796 | * |
||||||||
797 | * @return bool|int 0 success |
||||||||
798 | */ |
||||||||
799 | public function alterTable($table, $name, $owner, $schema, $comment, $tablespace, bool $with_oids = true) |
||||||||
800 | { |
||||||||
801 | $tblrs = $this->getTable($table); |
||||||||
802 | |||||||||
803 | if (1 !== $tblrs->recordCount()) { |
||||||||
804 | return -2; |
||||||||
805 | } |
||||||||
806 | |||||||||
807 | $status = $this->beginTransaction(); |
||||||||
808 | //dump(['beginTransaction' => $status]); |
||||||||
809 | |||||||||
810 | if (0 !== $status) { |
||||||||
811 | $this->rollbackTransaction(); |
||||||||
812 | |||||||||
813 | return -1; |
||||||||
814 | } |
||||||||
815 | $sql_sentence = "ALTER TABLE \"{$this->_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
816 | $status = $this->_alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace, !$with_oids, $sql_sentence); |
||||||||
0 ignored issues
–
show
The call to
PHPPgAdmin\Database\Trai...bleTrait::_alterTable() has too many arguments starting with $sql_sentence .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue. If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above. ![]() It seems like
$tblrs can also be of type integer ; however, parameter $tblrs of PHPPgAdmin\Database\Trai...bleTrait::_alterTable() does only seem to accept PHPPgAdmin\ADORecordSet , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||
817 | //dump(['_alterTable' => [$status, $sql_sentence.$this->getLastExecutedSQL()]]); |
||||||||
818 | |||||||||
819 | if (0 !== $status) { |
||||||||
820 | $this->rollbackTransaction(); |
||||||||
821 | |||||||||
822 | return $status; |
||||||||
823 | } |
||||||||
824 | |||||||||
825 | return $this->endTransaction(); |
||||||||
826 | } |
||||||||
827 | |||||||||
828 | /** |
||||||||
829 | * Enables or disables the oid system column to a table a table's owner |
||||||||
830 | * /!\ this function is called from _alterTable which take care of escaping fields. |
||||||||
831 | * |
||||||||
832 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable() |
||||||||
833 | * @param null|string $owner |
||||||||
834 | * @param bool $withoutoids |
||||||||
835 | * |
||||||||
836 | * @return array{0:int,1:string} [status:0 if successful, change_sql: changed attribute] |
||||||||
837 | */ |
||||||||
838 | public function alterTableOids($tblrs, bool $withoutoids = false): array |
||||||||
839 | { |
||||||||
840 | $status = 0; |
||||||||
841 | $change_sql = ''; |
||||||||
842 | // no changes. Return 0 |
||||||||
843 | if ((bool) ($this->hasObjectID($tblrs->fields['relname'])) !== !$withoutoids) { |
||||||||
844 | /* vars cleaned in _alterTable */ |
||||||||
845 | $f_schema = $this->_schema; |
||||||||
846 | $this->fieldClean($f_schema); |
||||||||
847 | |||||||||
848 | $alter_sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
849 | $change_sql = ' SET '; |
||||||||
850 | |||||||||
851 | if ($withoutoids) { |
||||||||
852 | $change_sql .= ' WITHOUT OIDS'; |
||||||||
853 | } else { |
||||||||
854 | $change_sql .= ' WITH OIDS'; |
||||||||
855 | } |
||||||||
856 | $sql = \implode(' ', [$alter_sql, $change_sql]); |
||||||||
857 | |||||||||
858 | $status = $this->execute($sql); |
||||||||
859 | } |
||||||||
860 | |||||||||
861 | return [$status, $change_sql]; |
||||||||
862 | } |
||||||||
863 | |||||||||
864 | /** |
||||||||
865 | * Alter a table's owner |
||||||||
866 | * /!\ this function is called from _alterTable which take care of escaping fields. |
||||||||
867 | * |
||||||||
868 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable() |
||||||||
869 | * @param null|string $owner |
||||||||
870 | * |
||||||||
871 | * @return array{0:int,1:string} [status:0 if successful, change_sql: changed attribute] |
||||||||
872 | */ |
||||||||
873 | public function alterTableOwner($tblrs, $owner = null): array |
||||||||
874 | { |
||||||||
875 | $status = 0; |
||||||||
876 | $change_sql = ''; |
||||||||
877 | /* vars cleaned in _alterTable */ |
||||||||
878 | if (!empty($owner) && ($tblrs->fields['relowner'] !== $owner)) { |
||||||||
879 | $f_schema = $this->_schema; |
||||||||
880 | $this->fieldClean($f_schema); |
||||||||
881 | // If owner has been changed, then do the alteration. We are |
||||||||
882 | // careful to avoid this generally as changing owner is a |
||||||||
883 | // superuser only function. |
||||||||
884 | $alter_sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
885 | $change_sql = " OWNER TO \"{$owner}\""; |
||||||||
886 | $sql = \implode(' ', [$alter_sql, $change_sql]); |
||||||||
887 | |||||||||
888 | $status = $this->execute($sql); |
||||||||
889 | } |
||||||||
890 | |||||||||
891 | return [$status, $change_sql]; |
||||||||
892 | } |
||||||||
893 | |||||||||
894 | /** |
||||||||
895 | * Alter a table's tablespace |
||||||||
896 | * /!\ this function is called from _alterTable which take care of escaping fields. |
||||||||
897 | * |
||||||||
898 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable() |
||||||||
899 | * @param null|string $tablespace |
||||||||
900 | * |
||||||||
901 | * @return array{0:int,1:string} [status:0 if successful, change_sql: changed attribute] |
||||||||
902 | */ |
||||||||
903 | public function alterTableTablespace($tblrs, $tablespace = null): array |
||||||||
904 | { |
||||||||
905 | $status = 0; |
||||||||
906 | $change_sql = ''; |
||||||||
907 | /* vars cleaned in _alterTable */ |
||||||||
908 | if (!empty($tablespace) && ($tblrs->fields['tablespace'] !== $tablespace)) { |
||||||||
909 | $f_schema = $this->_schema; |
||||||||
910 | $this->fieldClean($f_schema); |
||||||||
911 | |||||||||
912 | // If tablespace has been changed, then do the alteration. We |
||||||||
913 | // don't want to do this unnecessarily. |
||||||||
914 | $alter_sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
915 | $change_sql = " SET TABLESPACE \"{$tablespace}\""; |
||||||||
916 | $sql = \implode(' ', [$alter_sql, $change_sql]); |
||||||||
917 | |||||||||
918 | $status = $this->execute($sql); |
||||||||
919 | } |
||||||||
920 | |||||||||
921 | return [$status, $change_sql]; |
||||||||
922 | } |
||||||||
923 | |||||||||
924 | /** |
||||||||
925 | * Alter a table's name |
||||||||
926 | * /!\ this function is called from _alterTable which take care of escaping fields. |
||||||||
927 | * |
||||||||
928 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable() |
||||||||
929 | * @param string $name The new table's name |
||||||||
930 | * |
||||||||
931 | * @return array{0:int,1:string} [status:0 if successful, change_sql: changed attribute] |
||||||||
932 | */ |
||||||||
933 | public function alterTableName($tblrs, $name = null): array |
||||||||
934 | { |
||||||||
935 | $status = 0; |
||||||||
936 | $change_sql = ''; |
||||||||
937 | /* vars cleaned in _alterTable */ |
||||||||
938 | // Rename (only if name has changed) |
||||||||
939 | if (!empty($name) && ($name !== $tblrs->fields['relname'])) { |
||||||||
940 | $f_schema = $this->_schema; |
||||||||
941 | $this->fieldClean($f_schema); |
||||||||
942 | |||||||||
943 | $alter_sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
944 | $change_sql = " RENAME TO \"{$name}\""; |
||||||||
945 | $sql = \implode(' ', [$alter_sql, $change_sql]); |
||||||||
946 | $status = $this->execute($sql); |
||||||||
947 | |||||||||
948 | if (0 === $status) { |
||||||||
949 | $tblrs->fields['relname'] = $name; |
||||||||
950 | } |
||||||||
951 | } |
||||||||
952 | |||||||||
953 | return [$status, $change_sql]; |
||||||||
954 | } |
||||||||
955 | |||||||||
956 | // Row functions |
||||||||
957 | |||||||||
958 | /** |
||||||||
959 | * Alter a table's schema |
||||||||
960 | * /!\ this function is called from _alterTable which take care of escaping fields. |
||||||||
961 | * |
||||||||
962 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table RecordSet returned by getTable() |
||||||||
963 | * @param null|string $schema |
||||||||
964 | * |
||||||||
965 | * @return array{0:int,1:string} [status:0 if successful, change_sql: changed attribute] |
||||||||
966 | */ |
||||||||
967 | public function alterTableSchema($tblrs, $schema = null): array |
||||||||
968 | { |
||||||||
969 | $status = 0; |
||||||||
970 | $change_sql = ''; |
||||||||
971 | /* vars cleaned in _alterTable */ |
||||||||
972 | if (!empty($schema) && ($tblrs->fields['nspname'] !== $schema)) { |
||||||||
973 | $f_schema = $this->_schema; |
||||||||
974 | $this->fieldClean($f_schema); |
||||||||
975 | // If tablespace has been changed, then do the alteration. We |
||||||||
976 | // don't want to do this unnecessarily. |
||||||||
977 | $alter_sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" "; |
||||||||
978 | $change_sql = " SET SCHEMA \"{$schema}\""; |
||||||||
979 | $sql = \implode(' ', [$alter_sql, $change_sql]); |
||||||||
980 | |||||||||
981 | $status = $this->execute($sql); |
||||||||
982 | } |
||||||||
983 | |||||||||
984 | return [$status, $change_sql]; |
||||||||
985 | } |
||||||||
986 | |||||||||
987 | /** |
||||||||
988 | * Empties a table in the database. |
||||||||
989 | * |
||||||||
990 | * @param string $table The table to be emptied |
||||||||
991 | * @param bool $cascade True to cascade truncate, false to restrict |
||||||||
992 | * |
||||||||
993 | * @return array<integer,mixed|string> 0 if operation was successful |
||||||||
994 | */ |
||||||||
995 | public function emptyTable($table, $cascade) |
||||||||
996 | { |
||||||||
997 | $f_schema = $this->_schema; |
||||||||
998 | $this->fieldClean($f_schema); |
||||||||
999 | $this->fieldClean($table); |
||||||||
1000 | |||||||||
1001 | $sql = "TRUNCATE TABLE \"{$f_schema}\".\"{$table}\" "; |
||||||||
1002 | |||||||||
1003 | if ($cascade) { |
||||||||
1004 | $sql = $sql . ' CASCADE'; |
||||||||
1005 | } |
||||||||
1006 | |||||||||
1007 | $status = $this->execute($sql); |
||||||||
1008 | |||||||||
1009 | return [$status, $sql]; |
||||||||
1010 | } |
||||||||
1011 | |||||||||
1012 | /** |
||||||||
1013 | * Removes a table from the database. |
||||||||
1014 | * |
||||||||
1015 | * @param string $table The table to drop |
||||||||
1016 | * @param bool $cascade True to cascade drop, false to restrict |
||||||||
1017 | * |
||||||||
1018 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1019 | */ |
||||||||
1020 | public function dropTable($table, $cascade) |
||||||||
1021 | { |
||||||||
1022 | $f_schema = $this->_schema; |
||||||||
1023 | $this->fieldClean($f_schema); |
||||||||
1024 | $this->fieldClean($table); |
||||||||
1025 | |||||||||
1026 | $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\""; |
||||||||
1027 | |||||||||
1028 | if ($cascade) { |
||||||||
1029 | $sql .= ' CASCADE'; |
||||||||
1030 | } |
||||||||
1031 | |||||||||
1032 | return $this->execute($sql); |
||||||||
1033 | } |
||||||||
1034 | |||||||||
1035 | /** |
||||||||
1036 | * Sets up the data object for a dump. eg. Starts the appropriate |
||||||||
1037 | * transaction, sets variables, etc. |
||||||||
1038 | * |
||||||||
1039 | * @return int 0 success |
||||||||
1040 | */ |
||||||||
1041 | public function beginDump() |
||||||||
1042 | { |
||||||||
1043 | // Begin serializable transaction (to dump consistent data) |
||||||||
1044 | $status = $this->beginTransaction(); |
||||||||
1045 | |||||||||
1046 | if (0 !== $status) { |
||||||||
1047 | return -1; |
||||||||
1048 | } |
||||||||
1049 | |||||||||
1050 | // Set serializable |
||||||||
1051 | $sql = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'; |
||||||||
1052 | $status = $this->execute($sql); |
||||||||
1053 | |||||||||
1054 | if (0 !== $status) { |
||||||||
1055 | $this->rollbackTransaction(); |
||||||||
1056 | |||||||||
1057 | return -1; |
||||||||
1058 | } |
||||||||
1059 | |||||||||
1060 | // Set datestyle to ISO |
||||||||
1061 | $sql = 'SET DATESTYLE = ISO'; |
||||||||
1062 | $status = $this->execute($sql); |
||||||||
1063 | |||||||||
1064 | if (0 !== $status) { |
||||||||
1065 | $this->rollbackTransaction(); |
||||||||
1066 | |||||||||
1067 | return -1; |
||||||||
1068 | } |
||||||||
1069 | |||||||||
1070 | // Set extra_float_digits to 2 |
||||||||
1071 | $sql = 'SET extra_float_digits TO 2'; |
||||||||
1072 | $status = $this->execute($sql); |
||||||||
1073 | |||||||||
1074 | if (0 !== $status) { |
||||||||
1075 | $this->rollbackTransaction(); |
||||||||
1076 | |||||||||
1077 | return -1; |
||||||||
1078 | } |
||||||||
1079 | |||||||||
1080 | return 0; |
||||||||
1081 | } |
||||||||
1082 | |||||||||
1083 | /** |
||||||||
1084 | * Ends the data object for a dump. |
||||||||
1085 | * |
||||||||
1086 | * @return bool 0 success |
||||||||
1087 | */ |
||||||||
1088 | public function endDump() |
||||||||
1089 | { |
||||||||
1090 | return $this->endTransaction(); |
||||||||
1091 | } |
||||||||
1092 | |||||||||
1093 | /** |
||||||||
1094 | * Returns a recordset of all columns in a relation. Used for data export. |
||||||||
1095 | * |
||||||||
1096 | * @@ Note: Really needs to use a cursor |
||||||||
1097 | * |
||||||||
1098 | * @param string $relation The name of a relation |
||||||||
1099 | * @param bool $oids true to dump also the oids |
||||||||
1100 | * |
||||||||
1101 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1102 | */ |
||||||||
1103 | public function dumpRelation($relation, $oids) |
||||||||
1104 | { |
||||||||
1105 | $this->fieldClean($relation); |
||||||||
1106 | |||||||||
1107 | // Actually retrieve the rows |
||||||||
1108 | if ($oids) { |
||||||||
1109 | $oid_str = $this->id . ', '; |
||||||||
1110 | } else { |
||||||||
1111 | $oid_str = ''; |
||||||||
1112 | } |
||||||||
1113 | |||||||||
1114 | return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\""); |
||||||||
1115 | } |
||||||||
1116 | |||||||||
1117 | /** |
||||||||
1118 | * Returns all available autovacuum per table information. |
||||||||
1119 | * |
||||||||
1120 | * @param string $table if given, return autovacuum info for the given table or return all informations for all table |
||||||||
1121 | * |
||||||||
1122 | * @return \PHPPgAdmin\ArrayRecordSet A recordset |
||||||||
1123 | */ |
||||||||
1124 | public function getTableAutovacuum($table = '') |
||||||||
1125 | { |
||||||||
1126 | $sql = ''; |
||||||||
1127 | |||||||||
1128 | if ('' !== $table) { |
||||||||
1129 | $this->clean($table); |
||||||||
1130 | $c_schema = $this->_schema; |
||||||||
1131 | $this->clean($c_schema); |
||||||||
1132 | |||||||||
1133 | $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions |
||||||||
1134 | FROM pg_class c |
||||||||
1135 | LEFT JOIN pg_namespace n ON n.oid = c.relnamespace |
||||||||
1136 | WHERE c.relkind = 'r'::\"char\" |
||||||||
1137 | AND n.nspname NOT IN ('pg_catalog','information_schema') |
||||||||
1138 | AND c.reloptions IS NOT NULL |
||||||||
1139 | AND c.relname = '{$table}' AND n.nspname = '{$c_schema}' |
||||||||
1140 | ORDER BY nspname, relname"; |
||||||||
1141 | } else { |
||||||||
1142 | $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions |
||||||||
1143 | FROM pg_class c |
||||||||
1144 | LEFT JOIN pg_namespace n ON n.oid = c.relnamespace |
||||||||
1145 | WHERE c.relkind = 'r'::\"char\" |
||||||||
1146 | AND n.nspname NOT IN ('pg_catalog','information_schema') |
||||||||
1147 | AND c.reloptions IS NOT NULL |
||||||||
1148 | ORDER BY nspname, relname"; |
||||||||
1149 | } |
||||||||
1150 | |||||||||
1151 | /* tmp var to parse the results */ |
||||||||
1152 | $_autovacs = $this->selectSet($sql); |
||||||||
1153 | |||||||||
1154 | /* result aray to return as RS */ |
||||||||
1155 | $autovacs = []; |
||||||||
1156 | |||||||||
1157 | while (!$_autovacs->EOF) { |
||||||||
1158 | $_ = [ |
||||||||
1159 | 'nspname' => $_autovacs->fields['nspname'], |
||||||||
1160 | 'relname' => $_autovacs->fields['relname'], |
||||||||
1161 | ]; |
||||||||
1162 | |||||||||
1163 | foreach (\explode(',', $_autovacs->fields['reloptions']) as $var) { |
||||||||
1164 | [$o, $v] = \explode('=', $var); |
||||||||
1165 | $_[$o] = $v; |
||||||||
1166 | } |
||||||||
1167 | |||||||||
1168 | $autovacs[] = $_; |
||||||||
1169 | |||||||||
1170 | $_autovacs->moveNext(); |
||||||||
1171 | } |
||||||||
1172 | |||||||||
1173 | return new \PHPPgAdmin\ArrayRecordSet($autovacs); |
||||||||
1174 | } |
||||||||
1175 | |||||||||
1176 | /** |
||||||||
1177 | * Returns the SQL for changing the current user. |
||||||||
1178 | * |
||||||||
1179 | * @param string $user The user to change to |
||||||||
1180 | * |
||||||||
1181 | * @return string The SQL |
||||||||
1182 | */ |
||||||||
1183 | public function getChangeUserSQL($user) |
||||||||
1184 | { |
||||||||
1185 | $this->clean($user); |
||||||||
1186 | |||||||||
1187 | return "SET SESSION AUTHORIZATION '{$user}';"; |
||||||||
1188 | } |
||||||||
1189 | |||||||||
1190 | /** |
||||||||
1191 | * Returns all available autovacuum per table information. |
||||||||
1192 | * |
||||||||
1193 | * @param string $table table name |
||||||||
1194 | * @param bool $vacenabled true if vacuum is enabled |
||||||||
1195 | * @param int $vacthreshold vacuum threshold |
||||||||
1196 | * @param int $vacscalefactor vacuum scalefactor |
||||||||
1197 | * @param int $anathresold analyze threshold |
||||||||
1198 | * @param int $anascalefactor analyze scale factor |
||||||||
1199 | * @param int $vaccostdelay vacuum cost delay |
||||||||
1200 | * @param int $vaccostlimit vacuum cost limit |
||||||||
1201 | * |
||||||||
1202 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1203 | */ |
||||||||
1204 | public function saveAutovacuum( |
||||||||
1205 | $table, |
||||||||
1206 | $vacenabled, |
||||||||
1207 | $vacthreshold, |
||||||||
1208 | $vacscalefactor, |
||||||||
1209 | $anathresold, |
||||||||
1210 | $anascalefactor, |
||||||||
1211 | $vaccostdelay, |
||||||||
1212 | $vaccostlimit |
||||||||
1213 | ) { |
||||||||
1214 | $f_schema = $this->_schema; |
||||||||
1215 | $this->fieldClean($f_schema); |
||||||||
1216 | $this->fieldClean($table); |
||||||||
1217 | |||||||||
1218 | $params = []; |
||||||||
1219 | |||||||||
1220 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET ("; |
||||||||
1221 | |||||||||
1222 | if (!empty($vacenabled)) { |
||||||||
1223 | $this->clean($vacenabled); |
||||||||
1224 | $params[] = "autovacuum_enabled='{$vacenabled}'"; |
||||||||
1225 | } |
||||||||
1226 | |||||||||
1227 | if (!empty($vacthreshold)) { |
||||||||
1228 | $this->clean($vacthreshold); |
||||||||
1229 | $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'"; |
||||||||
1230 | } |
||||||||
1231 | |||||||||
1232 | if (!empty($vacscalefactor)) { |
||||||||
1233 | $this->clean($vacscalefactor); |
||||||||
1234 | $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'"; |
||||||||
1235 | } |
||||||||
1236 | |||||||||
1237 | if (!empty($anathresold)) { |
||||||||
1238 | $this->clean($anathresold); |
||||||||
1239 | $params[] = "autovacuum_analyze_threshold='{$anathresold}'"; |
||||||||
1240 | } |
||||||||
1241 | |||||||||
1242 | if (!empty($anascalefactor)) { |
||||||||
1243 | $this->clean($anascalefactor); |
||||||||
1244 | $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'"; |
||||||||
1245 | } |
||||||||
1246 | |||||||||
1247 | if (!empty($vaccostdelay)) { |
||||||||
1248 | $this->clean($vaccostdelay); |
||||||||
1249 | $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'"; |
||||||||
1250 | } |
||||||||
1251 | |||||||||
1252 | if (!empty($vaccostlimit)) { |
||||||||
1253 | $this->clean($vaccostlimit); |
||||||||
1254 | $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'"; |
||||||||
1255 | } |
||||||||
1256 | |||||||||
1257 | $sql = $sql . \implode(',', $params) . ');'; |
||||||||
1258 | |||||||||
1259 | return $this->execute($sql); |
||||||||
1260 | } |
||||||||
1261 | |||||||||
1262 | // Type conversion routines |
||||||||
1263 | |||||||||
1264 | /** |
||||||||
1265 | * Drops autovacuum config for a table. |
||||||||
1266 | * |
||||||||
1267 | * @param string $table The table |
||||||||
1268 | * |
||||||||
1269 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1270 | */ |
||||||||
1271 | public function dropAutovacuum($table) |
||||||||
1272 | { |
||||||||
1273 | $f_schema = $this->_schema; |
||||||||
1274 | $this->fieldClean($f_schema); |
||||||||
1275 | $this->fieldClean($table); |
||||||||
1276 | |||||||||
1277 | return $this->execute( |
||||||||
1278 | " |
||||||||
1279 | ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold, |
||||||||
1280 | autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor, |
||||||||
1281 | autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit |
||||||||
1282 | );" |
||||||||
1283 | ); |
||||||||
1284 | } |
||||||||
1285 | |||||||||
1286 | abstract public function formatType($typname, $typmod); |
||||||||
1287 | |||||||||
1288 | abstract public function hasGrantOption(); |
||||||||
1289 | |||||||||
1290 | abstract public function hasRoles(); |
||||||||
1291 | |||||||||
1292 | abstract public function fieldClean(&$str); |
||||||||
1293 | |||||||||
1294 | abstract public function beginTransaction(); |
||||||||
1295 | |||||||||
1296 | abstract public function rollbackTransaction(); |
||||||||
1297 | |||||||||
1298 | abstract public function endTransaction(); |
||||||||
1299 | |||||||||
1300 | abstract public function execute($sql); |
||||||||
1301 | |||||||||
1302 | abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null); |
||||||||
1303 | |||||||||
1304 | abstract public function selectSet($sql); |
||||||||
1305 | |||||||||
1306 | abstract public function clean(&$str); |
||||||||
1307 | |||||||||
1308 | abstract public function phpBool($parameter); |
||||||||
1309 | |||||||||
1310 | abstract public function hasCreateTableLikeWithConstraints(); |
||||||||
1311 | |||||||||
1312 | abstract public function hasCreateTableLikeWithIndexes(); |
||||||||
1313 | |||||||||
1314 | abstract public function hasTablespaces(); |
||||||||
1315 | |||||||||
1316 | abstract public function delete($table, $conditions, $schema = ''); |
||||||||
1317 | |||||||||
1318 | abstract public function fieldArrayClean(&$arr); |
||||||||
1319 | |||||||||
1320 | abstract public function hasCreateFieldWithConstraints(); |
||||||||
1321 | |||||||||
1322 | abstract public function getAttributeNames($table, $atts); |
||||||||
1323 | |||||||||
1324 | /** |
||||||||
1325 | * Protected method which alter a table |
||||||||
1326 | * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION. |
||||||||
1327 | * |
||||||||
1328 | * @param \PHPPgAdmin\ADORecordSet $tblrs The table recordSet returned by getTable() |
||||||||
1329 | * @param string $name The new name for the table |
||||||||
1330 | * @param string $owner The new owner for the table |
||||||||
1331 | * @param string $schema The new schema for the table |
||||||||
1332 | * @param string $comment The comment on the table |
||||||||
1333 | * @param string $tablespace The new tablespace for the table ('' means leave as is) |
||||||||
1334 | * @param bool $withoutoids If set to TRUE, will drop oids column |
||||||||
1335 | * |
||||||||
1336 | * @return int 0 success |
||||||||
1337 | */ |
||||||||
1338 | protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace, bool $withoutoids = false) |
||||||||
1339 | { |
||||||||
1340 | $this->fieldArrayClean($tblrs->fields); |
||||||||
1341 | |||||||||
1342 | // Comment |
||||||||
1343 | $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment); |
||||||||
1344 | //dump(['setComment' => $this->getLastExecutedSQL()]); |
||||||||
1345 | |||||||||
1346 | $this->lastExecutedSql = $this->getLastExecutedSQL(); |
||||||||
0 ignored issues
–
show
It seems like
getLastExecutedSQL() must be provided by classes using this trait. How about adding it as abstract method to this trait?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||||
1347 | |||||||||
1348 | if (0 !== $status) { |
||||||||
1349 | return -4; |
||||||||
1350 | } |
||||||||
1351 | |||||||||
1352 | // Owner |
||||||||
1353 | $this->fieldClean($owner); |
||||||||
1354 | [$TableOwnerStatus,$TableOwnerSQL] = $this->alterTableOwner($tblrs, $owner); |
||||||||
1355 | $this->lastExecutedSql .= $TableOwnerSQL ? \sprintf('%s%s', \PHP_EOL, $TableOwnerSQL) : ''; |
||||||||
1356 | //dump(['alterTableOwner' => [$TableOwnerStatus,$TableOwnerSQL]]); |
||||||||
1357 | |||||||||
1358 | if (0 !== $TableOwnerStatus) { |
||||||||
1359 | return -5; |
||||||||
1360 | } |
||||||||
1361 | |||||||||
1362 | // Tablespace |
||||||||
1363 | $this->fieldClean($tablespace); |
||||||||
1364 | [$TableTablespaceStatus,$TableTablespaceSQL] = $this->alterTableTablespace($tblrs, $tablespace); |
||||||||
1365 | $this->lastExecutedSql .= $TableTablespaceSQL ? \sprintf('%s%s', \PHP_EOL, $TableTablespaceSQL) : ''; |
||||||||
1366 | //dump(['alterTableTablespace' => [$TableTablespaceStatus,$TableTablespaceSQL]]); |
||||||||
1367 | |||||||||
1368 | if (0 !== $TableTablespaceStatus) { |
||||||||
1369 | return -6; |
||||||||
1370 | } |
||||||||
1371 | |||||||||
1372 | // Rename |
||||||||
1373 | $this->fieldClean($name); |
||||||||
1374 | [$TableNameStatus,$TableNameSQL] = $this->alterTableName($tblrs, $name); |
||||||||
1375 | $this->lastExecutedSql .= $TableNameSQL ? \sprintf('%s%s', \PHP_EOL, $TableNameSQL) : ''; |
||||||||
1376 | //dump(['alterTableName' => [$TableNameStatus,$TableNameSQL]]); |
||||||||
1377 | |||||||||
1378 | if (0 !== $TableNameStatus) { |
||||||||
1379 | return -3; |
||||||||
1380 | } |
||||||||
1381 | |||||||||
1382 | // Schema |
||||||||
1383 | $this->fieldClean($schema); |
||||||||
1384 | [$TableSchemaStatus,$TableSchemaSQL] = $this->alterTableSchema($tblrs, $schema); |
||||||||
1385 | $this->lastExecutedSql .= $TableSchemaSQL ? \sprintf('%s%s', \PHP_EOL, $TableSchemaSQL) : ''; |
||||||||
1386 | //dump(['alterTableSchema' => [$TableSchemaStatus,$TableSchemaSQL]]); |
||||||||
1387 | |||||||||
1388 | if (0 !== $TableSchemaStatus) { |
||||||||
1389 | return -7; |
||||||||
1390 | } |
||||||||
1391 | [$TableOidsStatus,$TableOidsSQL] = $this->alterTableOids($tblrs, $withoutoids); |
||||||||
1392 | $this->lastExecutedSql .= $TableOidsSQL ? \sprintf('%s%s', \PHP_EOL, $TableOidsSQL) : ''; |
||||||||
1393 | //dump(['alterTableOids' => [$TableOidsStatus,$TableOidsSQL]]); |
||||||||
1394 | if (0 !== $TableOidsStatus) { |
||||||||
1395 | return -7; |
||||||||
1396 | } |
||||||||
1397 | |||||||||
1398 | return 0; |
||||||||
1399 | } |
||||||||
1400 | |||||||||
1401 | /** |
||||||||
1402 | * Dumps serial-like columns in the table. |
||||||||
1403 | * |
||||||||
1404 | * @param \PHPPgAdmin\ADORecordSet $atts table attributes |
||||||||
1405 | * @param \PHPPgAdmin\ADORecordSet $tblfields table fields object |
||||||||
1406 | * @param string $sql The sql sentence |
||||||||
1407 | * generated so far |
||||||||
1408 | * @param string $col_comments_sql Column comments, |
||||||||
1409 | * passed by reference |
||||||||
1410 | * @param int $i current counter to |
||||||||
1411 | * know if we should |
||||||||
1412 | * append a comma to the |
||||||||
1413 | * sentence |
||||||||
1414 | * @param int $num Table attributes |
||||||||
1415 | * count + table |
||||||||
1416 | * constraints count |
||||||||
1417 | * |
||||||||
1418 | * @return string original $sql plus appended strings |
||||||||
1419 | */ |
||||||||
1420 | private function _dumpSerials($atts, $tblfields, $sql, &$col_comments_sql, $i, $num) |
||||||||
1421 | { |
||||||||
1422 | while (!$atts->EOF) { |
||||||||
1423 | $this->fieldClean($atts->fields['attname']); |
||||||||
1424 | $sql .= " \"{$atts->fields['attname']}\""; |
||||||||
1425 | // Dump SERIAL and BIGSERIAL columns correctly |
||||||||
1426 | if ($this->phpBool($atts->fields['attisserial']) && |
||||||||
1427 | ('integer' === $atts->fields['type'] || 'bigint' === $atts->fields['type']) |
||||||||
1428 | ) { |
||||||||
1429 | if ('integer' === $atts->fields['type']) { |
||||||||
1430 | $sql .= ' SERIAL'; |
||||||||
1431 | } else { |
||||||||
1432 | $sql .= ' BIGSERIAL'; |
||||||||
1433 | } |
||||||||
1434 | } else { |
||||||||
1435 | $sql .= ' ' . $this->formatType($atts->fields['type'], $atts->fields['atttypmod']); |
||||||||
1436 | |||||||||
1437 | // Add NOT NULL if necessary |
||||||||
1438 | if ($this->phpBool($atts->fields['attnotnull'])) { |
||||||||
1439 | $sql .= ' NOT NULL'; |
||||||||
1440 | } |
||||||||
1441 | |||||||||
1442 | // Add default if necessary |
||||||||
1443 | if (null !== $atts->fields['adsrc']) { |
||||||||
1444 | $sql .= " DEFAULT {$atts->fields['adsrc']}"; |
||||||||
1445 | } |
||||||||
1446 | } |
||||||||
1447 | |||||||||
1448 | // Output comma or not |
||||||||
1449 | if ($i < $num) { |
||||||||
1450 | $sql .= ",\n"; |
||||||||
1451 | } else { |
||||||||
1452 | $sql .= "\n"; |
||||||||
1453 | } |
||||||||
1454 | |||||||||
1455 | // Does this column have a comment? |
||||||||
1456 | if (null !== $atts->fields['comment']) { |
||||||||
1457 | $this->clean($atts->fields['comment']); |
||||||||
1458 | $col_comments_sql .= "COMMENT ON COLUMN \"{$tblfields->fields['relname']}\".\"{$atts->fields['attname']}\" IS '{$atts->fields['comment']}';\n"; |
||||||||
1459 | } |
||||||||
1460 | |||||||||
1461 | $atts->moveNext(); |
||||||||
1462 | ++$i; |
||||||||
1463 | } |
||||||||
1464 | |||||||||
1465 | return $sql; |
||||||||
1466 | } |
||||||||
1467 | |||||||||
1468 | /** |
||||||||
1469 | * Dumps constraints. |
||||||||
1470 | * |
||||||||
1471 | * @param \PHPPgAdmin\ADORecordSet $cons The table constraints |
||||||||
1472 | * @param string $table The table to define |
||||||||
1473 | * @param string $sql The sql sentence generated so |
||||||||
1474 | * far |
||||||||
1475 | * @param mixed $i |
||||||||
1476 | * @param int $num Table attributes count + table |
||||||||
1477 | * constraints count |
||||||||
1478 | */ |
||||||||
1479 | private function _dumpConstraints($cons, $table, $sql, $i, $num): ?string |
||||||||
1480 | { |
||||||||
1481 | // Output all table constraints |
||||||||
1482 | while (!$cons->EOF) { |
||||||||
1483 | $this->fieldClean($cons->fields['conname']); |
||||||||
1484 | $sql .= " CONSTRAINT \"{$cons->fields['conname']}\" "; |
||||||||
1485 | // Nasty hack to support pre-7.4 PostgreSQL |
||||||||
1486 | if (null !== $cons->fields['consrc']) { |
||||||||
1487 | $sql .= $cons->fields['consrc']; |
||||||||
1488 | } else { |
||||||||
1489 | switch ($cons->fields['contype']) { |
||||||||
1490 | case 'p': |
||||||||
1491 | $keys = $this->getAttributeNames($table, \explode(' ', $cons->fields['indkey'])); |
||||||||
1492 | $sql .= 'PRIMARY KEY (' . \implode(',', $keys) . ')'; |
||||||||
1493 | |||||||||
1494 | break; |
||||||||
1495 | case 'u': |
||||||||
1496 | $keys = $this->getAttributeNames($table, \explode(' ', $cons->fields['indkey'])); |
||||||||
1497 | $sql .= 'UNIQUE (' . \implode(',', $keys) . ')'; |
||||||||
1498 | |||||||||
1499 | break; |
||||||||
1500 | |||||||||
1501 | default: |
||||||||
1502 | // Unrecognised constraint |
||||||||
1503 | $this->rollbackTransaction(); |
||||||||
1504 | |||||||||
1505 | return null; |
||||||||
1506 | } |
||||||||
1507 | } |
||||||||
1508 | |||||||||
1509 | // Output comma or not |
||||||||
1510 | if ($i < $num) { |
||||||||
1511 | $sql .= ",\n"; |
||||||||
1512 | } else { |
||||||||
1513 | $sql .= "\n"; |
||||||||
1514 | } |
||||||||
1515 | |||||||||
1516 | $cons->moveNext(); |
||||||||
1517 | ++$i; |
||||||||
1518 | } |
||||||||
1519 | |||||||||
1520 | return $sql; |
||||||||
1521 | } |
||||||||
1522 | |||||||||
1523 | /** |
||||||||
1524 | * Dumps col statistics. |
||||||||
1525 | * |
||||||||
1526 | * @param \PHPPgAdmin\ADORecordSet $atts table attributes |
||||||||
1527 | * @param \PHPPgAdmin\ADORecordSet $tblfields table field attributes |
||||||||
1528 | * @param string $sql The sql sentence generated so far |
||||||||
1529 | */ |
||||||||
1530 | private function _dumpColStats($atts, $tblfields, $sql): ?string |
||||||||
1531 | { |
||||||||
1532 | // Column storage and statistics |
||||||||
1533 | $atts->moveFirst(); |
||||||||
1534 | $first = true; |
||||||||
1535 | |||||||||
1536 | while (!$atts->EOF) { |
||||||||
1537 | $this->fieldClean($atts->fields['attname']); |
||||||||
1538 | // Statistics first |
||||||||
1539 | if (0 <= $atts->fields['attstattarget']) { |
||||||||
1540 | if ($first) { |
||||||||
1541 | $sql .= "\n"; |
||||||||
1542 | $first = false; |
||||||||
1543 | } |
||||||||
1544 | $sql .= "ALTER TABLE ONLY \"{$tblfields->fields['nspname']}\".\"{$tblfields->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n"; |
||||||||
0 ignored issues
–
show
|
|||||||||
1545 | } |
||||||||
1546 | // Then storage |
||||||||
1547 | if ($atts->fields['attstorage'] !== $atts->fields['typstorage']) { |
||||||||
1548 | switch ($atts->fields['attstorage']) { |
||||||||
1549 | case 'p': |
||||||||
1550 | $storage = 'PLAIN'; |
||||||||
1551 | |||||||||
1552 | break; |
||||||||
1553 | case 'e': |
||||||||
1554 | $storage = 'EXTERNAL'; |
||||||||
1555 | |||||||||
1556 | break; |
||||||||
1557 | case 'm': |
||||||||
1558 | $storage = 'MAIN'; |
||||||||
1559 | |||||||||
1560 | break; |
||||||||
1561 | case 'x': |
||||||||
1562 | $storage = 'EXTENDED'; |
||||||||
1563 | |||||||||
1564 | break; |
||||||||
1565 | |||||||||
1566 | default: |
||||||||
1567 | // Unknown storage type |
||||||||
1568 | $this->rollbackTransaction(); |
||||||||
1569 | |||||||||
1570 | return null; |
||||||||
1571 | } |
||||||||
1572 | $sql .= "ALTER TABLE ONLY \"{$tblfields->fields['nspname']}\".\"{$tblfields->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n"; |
||||||||
1573 | } |
||||||||
1574 | |||||||||
1575 | $atts->moveNext(); |
||||||||
1576 | } |
||||||||
1577 | |||||||||
1578 | return $sql; |
||||||||
1579 | } |
||||||||
1580 | |||||||||
1581 | /** |
||||||||
1582 | * Dumps privileges. |
||||||||
1583 | * |
||||||||
1584 | * @param \PHPPgAdmin\ADORecordSet $privs The table privileges |
||||||||
1585 | * @param \PHPPgAdmin\ADORecordSet $tblfields The table fields definition |
||||||||
1586 | * @param string $sql The sql sentence generated so far |
||||||||
1587 | */ |
||||||||
1588 | private function _dumpPrivileges($privs, $tblfields, $sql): ?string |
||||||||
1589 | { |
||||||||
1590 | if (0 >= \count($privs)) { |
||||||||
1591 | return $sql; |
||||||||
1592 | } |
||||||||
1593 | $sql .= "\n-- Privileges\n\n"; |
||||||||
1594 | /* |
||||||||
1595 | * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to |
||||||||
1596 | * wire-in knowledge about the default public privileges for different |
||||||||
1597 | * kinds of objects. |
||||||||
1598 | */ |
||||||||
1599 | $sql .= "REVOKE ALL ON TABLE \"{$tblfields->fields['nspname']}\".\"{$tblfields->fields['relname']}\" FROM PUBLIC;\n"; |
||||||||
1600 | |||||||||
1601 | foreach ($privs as $v) { |
||||||||
1602 | // Get non-GRANT OPTION privs |
||||||||
1603 | $nongrant = \array_diff($v[2], $v[4]); |
||||||||
1604 | |||||||||
1605 | // Skip empty or owner ACEs |
||||||||
1606 | if (0 === \count($v[2]) || ('user' === $v[0] && $v[1] === $tblfields->fields['relowner'])) { |
||||||||
1607 | continue; |
||||||||
1608 | } |
||||||||
1609 | |||||||||
1610 | // Change user if necessary |
||||||||
1611 | if ($this->hasGrantOption() && $v[3] !== $tblfields->fields['relowner']) { |
||||||||
1612 | $grantor = $v[3]; |
||||||||
1613 | $this->clean($grantor); |
||||||||
1614 | $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n"; |
||||||||
1615 | } |
||||||||
1616 | |||||||||
1617 | // Output privileges with no GRANT OPTION |
||||||||
1618 | $sql .= 'GRANT ' . \implode(', ', $nongrant) . " ON TABLE \"{$tblfields->fields['relname']}\" TO "; |
||||||||
1619 | |||||||||
1620 | switch ($v[0]) { |
||||||||
1621 | case 'public': |
||||||||
1622 | $sql .= "PUBLIC;\n"; |
||||||||
1623 | |||||||||
1624 | break; |
||||||||
1625 | case 'user': |
||||||||
1626 | case 'role': |
||||||||
1627 | $this->fieldClean($v[1]); |
||||||||
1628 | $sql .= "\"{$v[1]}\";\n"; |
||||||||
1629 | |||||||||
1630 | break; |
||||||||
1631 | case 'group': |
||||||||
1632 | $this->fieldClean($v[1]); |
||||||||
1633 | $sql .= "GROUP \"{$v[1]}\";\n"; |
||||||||
1634 | |||||||||
1635 | break; |
||||||||
1636 | |||||||||
1637 | default: |
||||||||
1638 | // Unknown privilege type - fail |
||||||||
1639 | $this->rollbackTransaction(); |
||||||||
1640 | |||||||||
1641 | return null; |
||||||||
1642 | } |
||||||||
1643 | |||||||||
1644 | // Reset user if necessary |
||||||||
1645 | if ($this->hasGrantOption() && $v[3] !== $tblfields->fields['relowner']) { |
||||||||
1646 | $sql .= "RESET SESSION AUTHORIZATION;\n"; |
||||||||
1647 | } |
||||||||
1648 | |||||||||
1649 | // Output privileges with GRANT OPTION |
||||||||
1650 | |||||||||
1651 | // Skip empty or owner ACEs |
||||||||
1652 | if (!$this->hasGrantOption() || 0 === \count($v[4])) { |
||||||||
1653 | continue; |
||||||||
1654 | } |
||||||||
1655 | |||||||||
1656 | // Change user if necessary |
||||||||
1657 | if ($this->hasGrantOption() && $v[3] !== $tblfields->fields['relowner']) { |
||||||||
1658 | $grantor = $v[3]; |
||||||||
1659 | $this->clean($grantor); |
||||||||
1660 | $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n"; |
||||||||
1661 | } |
||||||||
1662 | |||||||||
1663 | $sql .= 'GRANT ' . \implode(', ', $v[4]) . " ON \"{$tblfields->fields['relname']}\" TO "; |
||||||||
1664 | |||||||||
1665 | switch ($v[0]) { |
||||||||
1666 | case 'public': |
||||||||
1667 | $sql .= 'PUBLIC'; |
||||||||
1668 | |||||||||
1669 | break; |
||||||||
1670 | case 'user': |
||||||||
1671 | case 'role': |
||||||||
1672 | $this->fieldClean($v[1]); |
||||||||
1673 | $sql .= "\"{$v[1]}\""; |
||||||||
1674 | |||||||||
1675 | break; |
||||||||
1676 | case 'group': |
||||||||
1677 | $this->fieldClean($v[1]); |
||||||||
1678 | $sql .= "GROUP \"{$v[1]}\""; |
||||||||
1679 | |||||||||
1680 | break; |
||||||||
1681 | |||||||||
1682 | default: |
||||||||
1683 | // Unknown privilege type - fail |
||||||||
1684 | return null; |
||||||||
1685 | } |
||||||||
1686 | $sql .= " WITH GRANT OPTION;\n"; |
||||||||
1687 | |||||||||
1688 | // Reset user if necessary |
||||||||
1689 | if ($this->hasGrantOption() && $v[3] !== $tblfields->fields['relowner']) { |
||||||||
1690 | $sql .= "RESET SESSION AUTHORIZATION;\n"; |
||||||||
1691 | } |
||||||||
1692 | } |
||||||||
1693 | |||||||||
1694 | return $sql; |
||||||||
1695 | } |
||||||||
1696 | |||||||||
1697 | /** |
||||||||
1698 | * Dumps a create. |
||||||||
1699 | * |
||||||||
1700 | * @param \PHPPgAdmin\ADORecordSet $tblfields table fields object |
||||||||
1701 | * @param string $sql The sql sentence generated so far |
||||||||
1702 | * @param string $cleanprefix set to '-- ' to avoid issuing DROP statement |
||||||||
1703 | * @param mixed $fields |
||||||||
1704 | * |
||||||||
1705 | * @return string original $sql plus appended strings |
||||||||
1706 | */ |
||||||||
1707 | private function _dumpCreate($tblfields, $sql, $cleanprefix) |
||||||||
1708 | { |
||||||||
1709 | // Set schema search path |
||||||||
1710 | $sql .= "SET search_path = \"{$tblfields->fields['nspname']}\", pg_catalog;\n\n"; |
||||||||
1711 | |||||||||
1712 | // Begin CREATE TABLE definition |
||||||||
1713 | $sql .= "-- Definition\n\n"; |
||||||||
1714 | // DROP TABLE must be fully qualified in case a table with the same name exists |
||||||||
1715 | $sql .= $cleanprefix . 'DROP TABLE '; |
||||||||
1716 | $sql .= "\"{$tblfields->fields['nspname']}\".\"{$tblfields->fields['relname']}\";\n"; |
||||||||
1717 | $sql .= "CREATE TABLE \"{$tblfields->fields['nspname']}\".\"{$tblfields->fields['relname']}\" (\n"; |
||||||||
1718 | |||||||||
1719 | return $sql; |
||||||||
1720 | } |
||||||||
1721 | |||||||||
1722 | /** |
||||||||
1723 | * Retrieve all attributes definition of a table. |
||||||||
1724 | * |
||||||||
1725 | * @param string $table The name of the table |
||||||||
1726 | * @param string $c_schema The name of the schema |
||||||||
1727 | * |
||||||||
1728 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1729 | */ |
||||||||
1730 | private function _getTableAttributesAll($table, $c_schema) |
||||||||
1731 | { |
||||||||
1732 | $sql = " |
||||||||
1733 | SELECT |
||||||||
1734 | a.attname, |
||||||||
1735 | a.attnum, |
||||||||
1736 | pg_catalog.format_type(a.atttypid, a.atttypmod) AS TYPE, |
||||||||
1737 | a.atttypmod, |
||||||||
1738 | a.attnotnull, |
||||||||
1739 | a.atthasdef, |
||||||||
1740 | pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, TRUE) AS adsrc, |
||||||||
1741 | a.attstattarget, |
||||||||
1742 | a.attstorage, |
||||||||
1743 | t.typstorage, |
||||||||
1744 | CASE |
||||||||
1745 | WHEN pc.oid IS NULL THEN FALSE |
||||||||
1746 | ELSE TRUE |
||||||||
1747 | END AS attisserial, |
||||||||
1748 | pg_catalog.col_description(a.attrelid, a.attnum) AS COMMENT |
||||||||
1749 | |||||||||
1750 | FROM pg_catalog.pg_tables tbl |
||||||||
1751 | JOIN pg_catalog.pg_class tbl_class ON tbl.tablename=tbl_class.relname |
||||||||
1752 | JOIN pg_catalog.pg_attribute a ON tbl_class.oid = a.attrelid |
||||||||
1753 | JOIN pg_catalog.pg_namespace ON pg_namespace.oid = tbl_class.relnamespace |
||||||||
1754 | AND pg_namespace.nspname=tbl.schemaname |
||||||||
1755 | LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid |
||||||||
1756 | AND a.attnum=adef.adnum |
||||||||
1757 | LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid |
||||||||
1758 | LEFT JOIN pg_catalog.pg_depend pd ON pd.refobjid=a.attrelid |
||||||||
1759 | AND pd.refobjsubid=a.attnum |
||||||||
1760 | AND pd.deptype='i' |
||||||||
1761 | LEFT JOIN pg_catalog.pg_class pc ON pd.objid=pc.oid |
||||||||
1762 | AND pd.classid=pc.tableoid |
||||||||
1763 | AND pd.refclassid=pc.tableoid |
||||||||
1764 | AND pc.relkind='S' |
||||||||
1765 | WHERE tbl.tablename='{$table}' |
||||||||
1766 | AND tbl.schemaname='{$c_schema}' |
||||||||
1767 | AND a.attnum > 0 AND NOT a.attisdropped |
||||||||
1768 | ORDER BY a.attnum"; |
||||||||
1769 | |||||||||
1770 | return $this->selectSet($sql); |
||||||||
1771 | } |
||||||||
1772 | |||||||||
1773 | /** |
||||||||
1774 | * Retrieve single attribute definition of a table. |
||||||||
1775 | * |
||||||||
1776 | * @param string $table The name of the table |
||||||||
1777 | * @param string $c_schema The schema of the table |
||||||||
1778 | * @param string $field (optional) The name of a field to return |
||||||||
1779 | * |
||||||||
1780 | * @return int|\PHPPgAdmin\ADORecordSet |
||||||||
1781 | */ |
||||||||
1782 | private function _getTableAttribute($table, $c_schema, $field) |
||||||||
1783 | { |
||||||||
1784 | $sql = " |
||||||||
1785 | SELECT |
||||||||
1786 | a.attname, a.attnum, |
||||||||
1787 | pg_catalog.format_type(a.atttypid, a.atttypmod) as type, |
||||||||
1788 | pg_catalog.format_type(a.atttypid, NULL) as base_type, |
||||||||
1789 | a.atttypmod, |
||||||||
1790 | a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc, |
||||||||
1791 | a.attstattarget, a.attstorage, t.typstorage, |
||||||||
1792 | pg_catalog.col_description(a.attrelid, a.attnum) AS comment |
||||||||
1793 | FROM |
||||||||
1794 | pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef |
||||||||
1795 | ON a.attrelid=adef.adrelid |
||||||||
1796 | AND a.attnum=adef.adnum |
||||||||
1797 | LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid |
||||||||
1798 | WHERE |
||||||||
1799 | a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||||||||
1800 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE |
||||||||
1801 | nspname = '{$c_schema}')) |
||||||||
1802 | AND a.attname = '{$field}'"; |
||||||||
1803 | |||||||||
1804 | return $this->selectSet($sql); |
||||||||
1805 | } |
||||||||
1806 | } |
||||||||
1807 |