Total Complexity | 1152 |
Total Lines | 9580 |
Duplicated Lines | 17.96 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like Postgres often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Postgres, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class Postgres extends ADOdbBase |
||
13 | { |
||
14 | use \PHPPgAdmin\HelperTrait; |
||
15 | |||
16 | public $major_version = 9.6; |
||
17 | // Max object name length |
||
18 | public $_maxNameLen = 63; |
||
19 | // Store the current schema |
||
20 | public $_schema; |
||
21 | // Map of database encoding names to HTTP encoding names. If a |
||
22 | // database encoding does not appear in this list, then its HTTP |
||
23 | // encoding name is the same as its database encoding name. |
||
24 | public $codemap = [ |
||
25 | 'BIG5' => 'BIG5', |
||
26 | 'EUC_CN' => 'GB2312', |
||
27 | 'EUC_JP' => 'EUC-JP', |
||
28 | 'EUC_KR' => 'EUC-KR', |
||
29 | 'EUC_TW' => 'EUC-TW', |
||
30 | 'GB18030' => 'GB18030', |
||
31 | 'GBK' => 'GB2312', |
||
32 | 'ISO_8859_5' => 'ISO-8859-5', |
||
33 | 'ISO_8859_6' => 'ISO-8859-6', |
||
34 | 'ISO_8859_7' => 'ISO-8859-7', |
||
35 | 'ISO_8859_8' => 'ISO-8859-8', |
||
36 | 'JOHAB' => 'CP1361', |
||
37 | 'KOI8' => 'KOI8-R', |
||
38 | 'LATIN1' => 'ISO-8859-1', |
||
39 | 'LATIN2' => 'ISO-8859-2', |
||
40 | 'LATIN3' => 'ISO-8859-3', |
||
41 | 'LATIN4' => 'ISO-8859-4', |
||
42 | 'LATIN5' => 'ISO-8859-9', |
||
43 | 'LATIN6' => 'ISO-8859-10', |
||
44 | 'LATIN7' => 'ISO-8859-13', |
||
45 | 'LATIN8' => 'ISO-8859-14', |
||
46 | 'LATIN9' => 'ISO-8859-15', |
||
47 | 'LATIN10' => 'ISO-8859-16', |
||
48 | 'SJIS' => 'SHIFT_JIS', |
||
49 | 'SQL_ASCII' => 'US-ASCII', |
||
50 | 'UHC' => 'WIN949', |
||
51 | 'UTF8' => 'UTF-8', |
||
52 | 'WIN866' => 'CP866', |
||
53 | 'WIN874' => 'CP874', |
||
54 | 'WIN1250' => 'CP1250', |
||
55 | 'WIN1251' => 'CP1251', |
||
56 | 'WIN1252' => 'CP1252', |
||
57 | 'WIN1256' => 'CP1256', |
||
58 | 'WIN1258' => 'CP1258', |
||
59 | ]; |
||
60 | public $defaultprops = ['', '', '']; |
||
61 | // Extra "magic" types. BIGSERIAL was added in PostgreSQL 7.2. |
||
62 | public $extraTypes = ['SERIAL', 'BIGSERIAL']; |
||
63 | // Foreign key stuff. First element MUST be the default. |
||
64 | public $fkactions = ['NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT']; |
||
65 | public $fkdeferrable = ['NOT DEFERRABLE', 'DEFERRABLE']; |
||
66 | public $fkinitial = ['INITIALLY IMMEDIATE', 'INITIALLY DEFERRED']; |
||
67 | public $fkmatches = ['MATCH SIMPLE', 'MATCH FULL']; |
||
68 | // Function properties |
||
69 | public $funcprops = [ |
||
70 | ['', 'VOLATILE', 'IMMUTABLE', 'STABLE'], |
||
71 | ['', 'CALLED ON NULL INPUT', 'RETURNS NULL ON NULL INPUT'], |
||
72 | ['', 'SECURITY INVOKER', 'SECURITY DEFINER'], |
||
73 | ]; |
||
74 | |||
75 | // Default help URL |
||
76 | public $help_base = null; |
||
77 | // Help sub pages |
||
78 | public $help_page = null; |
||
79 | // Name of id column |
||
80 | public $id = 'oid'; |
||
81 | |||
82 | // Supported join operations for use with view wizard |
||
83 | public $joinOps = ['INNER JOIN' => 'INNER JOIN', 'LEFT JOIN' => 'LEFT JOIN', 'RIGHT JOIN' => 'RIGHT JOIN', 'FULL JOIN' => 'FULL JOIN']; |
||
84 | // Map of internal language name to syntax highlighting name |
||
85 | public $langmap = [ |
||
86 | 'sql' => 'SQL', |
||
87 | 'plpgsql' => 'SQL', |
||
88 | 'php' => 'PHP', |
||
89 | 'phpu' => 'PHP', |
||
90 | 'plphp' => 'PHP', |
||
91 | 'plphpu' => 'PHP', |
||
92 | 'perl' => 'Perl', |
||
93 | 'perlu' => 'Perl', |
||
94 | 'plperl' => 'Perl', |
||
95 | 'plperlu' => 'Perl', |
||
96 | 'java' => 'Java', |
||
97 | 'javau' => 'Java', |
||
98 | 'pljava' => 'Java', |
||
99 | 'pljavau' => 'Java', |
||
100 | 'plj' => 'Java', |
||
101 | 'plju' => 'Java', |
||
102 | 'python' => 'Python', |
||
103 | 'pythonu' => 'Python', |
||
104 | 'plpython' => 'Python', |
||
105 | 'plpythonu' => 'Python', |
||
106 | 'ruby' => 'Ruby', |
||
107 | 'rubyu' => 'Ruby', |
||
108 | 'plruby' => 'Ruby', |
||
109 | 'plrubyu' => 'Ruby', |
||
110 | ]; |
||
111 | // Predefined size types |
||
112 | public $predefined_size_types = [ |
||
113 | 'abstime', |
||
114 | 'aclitem', |
||
115 | 'bigserial', |
||
116 | 'boolean', |
||
117 | 'bytea', |
||
118 | 'cid', |
||
119 | 'cidr', |
||
120 | 'circle', |
||
121 | 'date', |
||
122 | 'float4', |
||
123 | 'float8', |
||
124 | 'gtsvector', |
||
125 | 'inet', |
||
126 | 'int2', |
||
127 | 'int4', |
||
128 | 'int8', |
||
129 | 'macaddr', |
||
130 | 'money', |
||
131 | 'oid', |
||
132 | 'path', |
||
133 | 'polygon', |
||
134 | 'refcursor', |
||
135 | 'regclass', |
||
136 | 'regoper', |
||
137 | 'regoperator', |
||
138 | 'regproc', |
||
139 | 'regprocedure', |
||
140 | 'regtype', |
||
141 | 'reltime', |
||
142 | 'serial', |
||
143 | 'smgr', |
||
144 | 'text', |
||
145 | 'tid', |
||
146 | 'tinterval', |
||
147 | 'tsquery', |
||
148 | 'tsvector', |
||
149 | 'varbit', |
||
150 | 'void', |
||
151 | 'xid', |
||
152 | ]; |
||
153 | // List of all legal privileges that can be applied to different types |
||
154 | // of objects. |
||
155 | public $privlist = [ |
||
156 | 'table' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'], |
||
157 | 'view' => ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'], |
||
158 | 'sequence' => ['SELECT', 'UPDATE', 'ALL PRIVILEGES'], |
||
159 | 'database' => ['CREATE', 'TEMPORARY', 'CONNECT', 'ALL PRIVILEGES'], |
||
160 | 'function' => ['EXECUTE', 'ALL PRIVILEGES'], |
||
161 | 'language' => ['USAGE', 'ALL PRIVILEGES'], |
||
162 | 'schema' => ['CREATE', 'USAGE', 'ALL PRIVILEGES'], |
||
163 | 'tablespace' => ['CREATE', 'ALL PRIVILEGES'], |
||
164 | 'column' => ['SELECT', 'INSERT', 'UPDATE', 'REFERENCES', 'ALL PRIVILEGES'], |
||
165 | ]; |
||
166 | // List of characters in acl lists and the privileges they |
||
167 | // refer to. |
||
168 | public $privmap = [ |
||
169 | 'r' => 'SELECT', |
||
170 | 'w' => 'UPDATE', |
||
171 | 'a' => 'INSERT', |
||
172 | 'd' => 'DELETE', |
||
173 | 'D' => 'TRUNCATE', |
||
174 | 'R' => 'RULE', |
||
175 | 'x' => 'REFERENCES', |
||
176 | 't' => 'TRIGGER', |
||
177 | 'X' => 'EXECUTE', |
||
178 | 'U' => 'USAGE', |
||
179 | 'C' => 'CREATE', |
||
180 | 'T' => 'TEMPORARY', |
||
181 | 'c' => 'CONNECT', |
||
182 | ]; |
||
183 | // Rule action types |
||
184 | public $rule_events = ['SELECT', 'INSERT', 'UPDATE', 'DELETE']; |
||
185 | // Select operators |
||
186 | public $selectOps = [ |
||
187 | '=' => 'i', |
||
188 | '!=' => 'i', |
||
189 | '<' => 'i', |
||
190 | '>' => 'i', |
||
191 | '<=' => 'i', |
||
192 | '>=' => 'i', |
||
193 | '<<' => 'i', |
||
194 | '>>' => 'i', |
||
195 | '<<=' => 'i', |
||
196 | '>>=' => 'i', |
||
197 | 'LIKE' => 'i', |
||
198 | 'NOT LIKE' => 'i', |
||
199 | 'ILIKE' => 'i', |
||
200 | 'NOT ILIKE' => 'i', |
||
201 | 'SIMILAR TO' => 'i', |
||
202 | 'NOT SIMILAR TO' => 'i', |
||
203 | '~' => 'i', |
||
204 | '!~' => 'i', |
||
205 | '~*' => 'i', |
||
206 | '!~*' => 'i', |
||
207 | 'IS NULL' => 'p', |
||
208 | 'IS NOT NULL' => 'p', |
||
209 | 'IN' => 'x', |
||
210 | 'NOT IN' => 'x', |
||
211 | '@@' => 'i', |
||
212 | '@@@' => 'i', |
||
213 | '@>' => 'i', |
||
214 | '<@' => 'i', |
||
215 | '@@ to_tsquery' => 't', |
||
216 | '@@@ to_tsquery' => 't', |
||
217 | '@> to_tsquery' => 't', |
||
218 | '<@ to_tsquery' => 't', |
||
219 | '@@ plainto_tsquery' => 't', |
||
220 | '@@@ plainto_tsquery' => 't', |
||
221 | '@> plainto_tsquery' => 't', |
||
222 | '<@ plainto_tsquery' => 't', |
||
223 | ]; |
||
224 | // Array of allowed trigger events |
||
225 | public $triggerEvents = [ |
||
226 | 'INSERT', |
||
227 | 'UPDATE', |
||
228 | 'DELETE', |
||
229 | 'INSERT OR UPDATE', |
||
230 | 'INSERT OR DELETE', |
||
231 | 'DELETE OR UPDATE', |
||
232 | 'INSERT OR DELETE OR UPDATE', |
||
233 | ]; |
||
234 | // When to execute the trigger |
||
235 | public $triggerExecTimes = ['BEFORE', 'AFTER']; |
||
236 | // How often to execute the trigger |
||
237 | public $triggerFrequency = ['ROW', 'STATEMENT']; |
||
238 | // Array of allowed type alignments |
||
239 | public $typAligns = ['char', 'int2', 'int4', 'double']; |
||
240 | // The default type alignment |
||
241 | public $typAlignDef = 'int4'; |
||
242 | // Default index type |
||
243 | public $typIndexDef = 'BTREE'; |
||
244 | // Array of allowed index types |
||
245 | public $typIndexes = ['BTREE', 'RTREE', 'GIST', 'GIN', 'HASH']; |
||
246 | // Array of allowed type storage attributes |
||
247 | public $typStorages = ['plain', 'external', 'extended', 'main']; |
||
248 | // The default type storage |
||
249 | public $typStorageDef = 'plain'; |
||
250 | |||
251 | public function __construct(&$conn, $conf) |
||
252 | { |
||
253 | //$this->prtrace('major_version :' . $this->major_version); |
||
254 | $this->conn = $conn; |
||
255 | $this->conf = $conf; |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * Fetch a URL (or array of URLs) for a given help page. |
||
260 | * |
||
261 | * @param $help |
||
262 | * @return array|null|string |
||
263 | */ |
||
264 | public function getHelp($help) |
||
265 | { |
||
266 | $this->getHelpPages(); |
||
267 | |||
268 | if (isset($this->help_page[$help])) { |
||
269 | if (is_array($this->help_page[$help])) { |
||
270 | $urls = []; |
||
271 | foreach ($this->help_page[$help] as $link) { |
||
272 | $urls[] = $this->help_base . $link; |
||
273 | } |
||
274 | |||
275 | return $urls; |
||
276 | } |
||
277 | |||
278 | return $this->help_base . $this->help_page[$help]; |
||
279 | } else { |
||
280 | return null; |
||
281 | } |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * get help page by instancing the corresponding help class |
||
286 | * if $this->help_page and $this->help_base are set, this function is a noop |
||
287 | */ |
||
288 | public function getHelpPages() |
||
289 | { |
||
290 | if ($this->help_page === null || $this->help_base === null) { |
||
291 | $help_classname = '\PHPPgAdmin\Help\PostgresDoc' . str_replace('.', '', $this->major_version); |
||
292 | |||
293 | $help_class = new $help_classname($this->conf, $this->major_version); |
||
294 | |||
295 | $this->help_base = $help_class->getHelpBase(); |
||
296 | $this->help_page = $help_class->getHelpPage(); |
||
297 | } |
||
298 | } |
||
299 | |||
300 | // Formatting functions |
||
301 | |||
302 | /** |
||
303 | * Outputs the HTML code for a particular field |
||
304 | * |
||
305 | * @param $name The name to give the field |
||
306 | * @param $value The value of the field. Note this could be 'numeric(7,2)' sort of thing... |
||
307 | * @param $type The database type of the field |
||
308 | * @param array|\PHPPgAdmin\Database\An $extras An array of attributes name as key and attributes' values as value |
||
309 | */ |
||
310 | public function printField($name, $value, $type, $extras = []) |
||
372 | } |
||
373 | } |
||
374 | |||
375 | /** |
||
376 | * Escapes bytea data for display on the screen |
||
377 | * |
||
378 | * @param $data The bytea data |
||
379 | * @return Data formatted for on-screen display |
||
380 | */ |
||
381 | public function escapeBytea($data) |
||
382 | { |
||
383 | return htmlentities($data, ENT_QUOTES, 'UTF-8'); |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * Return all information about a particular database |
||
388 | * |
||
389 | * @param $database The name of the database to retrieve |
||
390 | * @return The database info |
||
391 | */ |
||
392 | public function getDatabase($database) |
||
393 | { |
||
394 | $this->clean($database); |
||
395 | $sql = "SELECT * FROM pg_database WHERE datname='{$database}'"; |
||
396 | |||
397 | return $this->selectSet($sql); |
||
398 | } |
||
399 | |||
400 | /** |
||
401 | * Cleans (escapes) a string |
||
402 | * |
||
403 | * @param $str The string to clean, by reference |
||
404 | * @return The cleaned string |
||
405 | */ |
||
406 | public function clean(&$str) |
||
407 | { |
||
408 | if ($str === null) { |
||
409 | return null; |
||
410 | } |
||
411 | |||
412 | $str = str_replace("\r\n", "\n", $str); |
||
413 | $str = pg_escape_string($str); |
||
414 | |||
415 | return $str; |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * Return all database available on the server |
||
420 | * |
||
421 | * @param $currentdatabase database name that should be on top of the resultset |
||
422 | * |
||
423 | * @return A list of databases, sorted alphabetically |
||
424 | */ |
||
425 | public function getDatabases($currentdatabase = null) |
||
426 | { |
||
427 | $conf = $this->conf; |
||
428 | $server_info = $this->server_info; |
||
429 | |||
430 | if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser()) { |
||
431 | $username = $server_info['username']; |
||
432 | $this->clean($username); |
||
433 | $clause = " AND pr.rolname='{$username}'"; |
||
434 | } else { |
||
435 | $clause = ''; |
||
436 | } |
||
437 | if (isset($server_info['useonlydefaultdb']) && $server_info['useonlydefaultdb']) { |
||
438 | $currentdatabase = $server_info['defaultdb']; |
||
439 | $clause .= " AND pdb.datname = '{$currentdatabase}' "; |
||
440 | } |
||
441 | |||
442 | if (isset($server_info['hiddendbs']) && $server_info['hiddendbs']) { |
||
443 | $hiddendbs = $server_info['hiddendbs']; |
||
444 | $not_in = "('" . implode("','", $hiddendbs) . "')"; |
||
445 | $clause .= " AND pdb.datname NOT IN {$not_in} "; |
||
446 | } |
||
447 | |||
448 | if ($currentdatabase != null) { |
||
449 | $this->clean($currentdatabase); |
||
450 | $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname"; |
||
451 | } else { |
||
452 | $orderby = 'ORDER BY pdb.datname'; |
||
453 | } |
||
454 | |||
455 | if (!$conf['show_system']) { |
||
456 | $where = ' AND NOT pdb.datistemplate'; |
||
457 | } else { |
||
458 | $where = ' AND pdb.datallowconn'; |
||
459 | } |
||
460 | |||
461 | $sql = " |
||
462 | SELECT pdb.datname AS datname, |
||
463 | pr.rolname AS datowner, |
||
464 | pg_encoding_to_char(encoding) AS datencoding, |
||
465 | (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid AND pd.classoid='pg_database'::regclass) AS datcomment, |
||
466 | (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace, |
||
467 | CASE WHEN pg_catalog.has_database_privilege(current_user, pdb.oid, 'CONNECT') |
||
468 | THEN pg_catalog.pg_database_size(pdb.oid) |
||
469 | ELSE -1 -- set this magic value, which we will convert to no access later |
||
470 | END as dbsize, |
||
471 | pdb.datcollate, |
||
472 | pdb.datctype |
||
473 | FROM pg_catalog.pg_database pdb |
||
474 | LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid) |
||
475 | WHERE true |
||
476 | {$where} |
||
477 | {$clause} |
||
478 | {$orderby}"; |
||
479 | |||
480 | return $this->selectSet($sql); |
||
481 | } |
||
482 | |||
483 | /** |
||
484 | * Determines whether or not a user is a super user |
||
485 | * |
||
486 | * @param \PHPPgAdmin\Database\The|string $username The username of the user |
||
487 | * @return True if is a super user, false otherwise |
||
488 | */ |
||
489 | public function isSuperUser($username = '') |
||
490 | { |
||
491 | $this->clean($username); |
||
492 | |||
493 | if (empty($usename)) { |
||
494 | $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser'); |
||
495 | if ($val !== false) { |
||
496 | return $val == 'on'; |
||
497 | } |
||
498 | } |
||
499 | |||
500 | $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'"; |
||
501 | |||
502 | $usesuper = $this->selectField($sql, 'usesuper'); |
||
503 | if ($usesuper == -1) { |
||
504 | return false; |
||
505 | } |
||
506 | |||
507 | return $usesuper == 't'; |
||
508 | } |
||
509 | |||
510 | /** |
||
511 | * Return the database comment of a db from the shared description table |
||
512 | * |
||
513 | * @param string $database the name of the database to get the comment for |
||
514 | * @return recordset of the db comment info |
||
515 | */ |
||
516 | public function getDatabaseComment($database) |
||
517 | { |
||
518 | $this->clean($database); |
||
519 | $sql = |
||
520 | "SELECT description FROM pg_catalog.pg_database JOIN pg_catalog.pg_shdescription ON (oid=objoid AND classoid='pg_database'::regclass) WHERE pg_database.datname = '{$database}' "; |
||
521 | |||
522 | return $this->selectSet($sql); |
||
523 | } |
||
524 | |||
525 | /** |
||
526 | * Return the database owner of a db |
||
527 | * |
||
528 | * @param string $database the name of the database to get the owner for |
||
529 | * @return recordset of the db owner info |
||
530 | */ |
||
531 | public function getDatabaseOwner($database) |
||
532 | { |
||
533 | $this->clean($database); |
||
534 | $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' "; |
||
535 | |||
536 | return $this->selectSet($sql); |
||
537 | } |
||
538 | |||
539 | // Help functions |
||
540 | |||
541 | // Database functions |
||
542 | |||
543 | /** |
||
544 | * Returns the current database encoding |
||
545 | * |
||
546 | * @return The encoding. eg. SQL_ASCII, UTF-8, etc. |
||
547 | */ |
||
548 | public function getDatabaseEncoding() |
||
549 | { |
||
550 | return pg_parameter_status($this->conn->_connectionID, 'server_encoding'); |
||
551 | } |
||
552 | |||
553 | /** |
||
554 | * Returns the current default_with_oids setting |
||
555 | * |
||
556 | * @return default_with_oids setting |
||
557 | */ |
||
558 | public function getDefaultWithOid() |
||
559 | { |
||
560 | $sql = 'SHOW default_with_oids'; |
||
561 | |||
562 | return $this->selectField($sql, 'default_with_oids'); |
||
563 | } |
||
564 | |||
565 | /** |
||
566 | * Creates a database |
||
567 | * |
||
568 | * @param $database The name of the database to create |
||
569 | * @param $encoding Encoding of the database |
||
570 | * @param string $tablespace (optional) The tablespace name |
||
571 | * @param string $comment |
||
572 | * @param string $template |
||
573 | * @param string $lc_collate |
||
574 | * @param string $lc_ctype |
||
575 | * @return int 0 success |
||
576 | */ |
||
577 | public function createDatabase( |
||
578 | $database, |
||
579 | $encoding, |
||
580 | $tablespace = '', |
||
581 | $comment = '', |
||
582 | $template = 'template1', |
||
583 | $lc_collate = '', |
||
584 | $lc_ctype = '' |
||
585 | ) { |
||
586 | $this->fieldClean($database); |
||
587 | $this->clean($encoding); |
||
588 | $this->fieldClean($tablespace); |
||
589 | $this->fieldClean($template); |
||
590 | $this->clean($lc_collate); |
||
591 | $this->clean($lc_ctype); |
||
592 | |||
593 | $sql = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=\"{$template}\""; |
||
594 | |||
595 | if ($encoding != '') { |
||
596 | $sql .= " ENCODING='{$encoding}'"; |
||
597 | } |
||
598 | |||
599 | if ($lc_collate != '') { |
||
600 | $sql .= " LC_COLLATE='{$lc_collate}'"; |
||
601 | } |
||
602 | |||
603 | if ($lc_ctype != '') { |
||
604 | $sql .= " LC_CTYPE='{$lc_ctype}'"; |
||
605 | } |
||
606 | |||
607 | if ($tablespace != '' && $this->hasTablespaces()) { |
||
608 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||
609 | } |
||
610 | |||
611 | $status = $this->execute($sql); |
||
612 | if ($status != 0) { |
||
613 | return -1; |
||
614 | } |
||
615 | |||
616 | View Code Duplication | if ($comment != '' && $this->hasSharedComments()) { |
|
617 | $status = $this->setComment('DATABASE', $database, '', $comment); |
||
618 | if ($status != 0) { |
||
619 | return -2; |
||
620 | } |
||
621 | } |
||
622 | |||
623 | return 0; |
||
624 | } |
||
625 | |||
626 | /** |
||
627 | * Cleans (escapes) an object name (eg. table, field) |
||
628 | * |
||
629 | * @param $str The string to clean, by reference |
||
630 | * @return The cleaned string |
||
631 | */ |
||
632 | public function fieldClean(&$str) |
||
633 | { |
||
634 | if ($str === null) { |
||
635 | return null; |
||
636 | } |
||
637 | |||
638 | $str = str_replace('"', '""', $str); |
||
639 | |||
640 | return $str; |
||
641 | } |
||
642 | |||
643 | public function hasTablespaces() |
||
644 | { |
||
645 | return true; |
||
646 | } |
||
647 | |||
648 | public function hasSharedComments() |
||
649 | { |
||
650 | return true; |
||
651 | } |
||
652 | |||
653 | /** |
||
654 | * Sets the comment for an object in the database |
||
655 | * |
||
656 | * @pre All parameters must already be cleaned |
||
657 | * @param $obj_type One of 'TABLE' | 'COLUMN' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'TYPE' | 'FUNCTION' | 'AGGREGATE' |
||
658 | * @param $obj_name The name of the object for which to attach a comment. |
||
659 | * @param $table Name of table that $obj_name belongs to. Ignored unless $obj_type is 'TABLE' or 'COLUMN'. |
||
660 | * @param $comment The comment to add. |
||
661 | * @param null $basetype |
||
662 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
663 | */ |
||
664 | public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null) |
||
665 | { |
||
666 | $sql = "COMMENT ON {$obj_type} "; |
||
667 | $f_schema = $this->_schema; |
||
668 | $this->fieldClean($f_schema); |
||
669 | $this->clean($comment); // Passing in an already cleaned comment will lead to double escaped data |
||
670 | // So, while counter-intuitive, it is important to not clean comments before |
||
671 | // calling setComment. We will clean it here instead. |
||
672 | /* |
||
673 | $this->fieldClean($table); |
||
674 | $this->fieldClean($obj_name); |
||
675 | */ |
||
676 | |||
677 | switch ($obj_type) { |
||
678 | case 'TABLE': |
||
679 | $sql .= "\"{$f_schema}\".\"{$table}\" IS "; |
||
680 | break; |
||
681 | case 'COLUMN': |
||
682 | $sql .= "\"{$f_schema}\".\"{$table}\".\"{$obj_name}\" IS "; |
||
683 | break; |
||
684 | case 'SEQUENCE': |
||
685 | case 'VIEW': |
||
686 | case 'TEXT SEARCH CONFIGURATION': |
||
687 | case 'TEXT SEARCH DICTIONARY': |
||
688 | case 'TEXT SEARCH TEMPLATE': |
||
689 | case 'TEXT SEARCH PARSER': |
||
690 | case 'TYPE': |
||
691 | $sql .= "\"{$f_schema}\"."; |
||
692 | case 'DATABASE': |
||
693 | case 'ROLE': |
||
694 | case 'SCHEMA': |
||
695 | case 'TABLESPACE': |
||
696 | $sql .= "\"{$obj_name}\" IS "; |
||
697 | break; |
||
698 | case 'FUNCTION': |
||
699 | $sql .= "\"{$f_schema}\".{$obj_name} IS "; |
||
700 | break; |
||
701 | case 'AGGREGATE': |
||
702 | $sql .= "\"{$f_schema}\".\"{$obj_name}\" (\"{$basetype}\") IS "; |
||
703 | break; |
||
704 | default: |
||
705 | // Unknown object type |
||
706 | return -1; |
||
707 | } |
||
708 | |||
709 | if ($comment != '') { |
||
710 | $sql .= "'{$comment}';"; |
||
711 | } else { |
||
712 | $sql .= 'NULL;'; |
||
713 | } |
||
714 | |||
715 | return $this->execute($sql); |
||
716 | } |
||
717 | |||
718 | /** |
||
719 | * Drops a database |
||
720 | * |
||
721 | * @param $database The name of the database to drop |
||
722 | * @return \PHPPgAdmin\Database\A 0 success |
||
723 | */ |
||
724 | public function dropDatabase($database) |
||
725 | { |
||
726 | $this->fieldClean($database); |
||
727 | $sql = "DROP DATABASE \"{$database}\""; |
||
728 | |||
729 | return $this->execute($sql); |
||
730 | } |
||
731 | |||
732 | /** |
||
733 | * Alters a database |
||
734 | * the multiple return vals are for postgres 8+ which support more functionality in alter database |
||
735 | * |
||
736 | * @param $dbName The name of the database |
||
737 | * @param $newName new name for the database |
||
738 | * @param \PHPPgAdmin\Database\The|string $newOwner The new owner for the database |
||
739 | * @param string $comment |
||
740 | * @return bool|int 0 success |
||
741 | */ |
||
742 | public function alterDatabase($dbName, $newName, $newOwner = '', $comment = '') |
||
743 | { |
||
744 | $status = $this->beginTransaction(); |
||
745 | if ($status != 0) { |
||
746 | $this->rollbackTransaction(); |
||
747 | |||
748 | return -1; |
||
749 | } |
||
750 | |||
751 | View Code Duplication | if ($dbName != $newName) { |
|
752 | $status = $this->alterDatabaseRename($dbName, $newName); |
||
753 | if ($status != 0) { |
||
754 | $this->rollbackTransaction(); |
||
755 | |||
756 | return -3; |
||
757 | } |
||
758 | $dbName = $newName; |
||
759 | } |
||
760 | |||
761 | if ($newOwner != '') { |
||
762 | $status = $this->alterDatabaseOwner($newName, $newOwner); |
||
763 | if ($status != 0) { |
||
764 | $this->rollbackTransaction(); |
||
765 | |||
766 | return -2; |
||
767 | } |
||
768 | } |
||
769 | |||
770 | $this->fieldClean($dbName); |
||
771 | $status = $this->setComment('DATABASE', $dbName, '', $comment); |
||
772 | if ($status != 0) { |
||
773 | $this->rollbackTransaction(); |
||
774 | |||
775 | return -4; |
||
776 | } |
||
777 | |||
778 | return $this->endTransaction(); |
||
779 | } |
||
780 | |||
781 | /** |
||
782 | * Renames a database, note that this operation cannot be |
||
783 | * performed on a database that is currently being connected to |
||
784 | * |
||
785 | * @param string $oldName name of database to rename |
||
786 | * @param string $newName new name of database |
||
787 | * @return int 0 on success |
||
788 | */ |
||
789 | public function alterDatabaseRename($oldName, $newName) |
||
790 | { |
||
791 | $this->fieldClean($oldName); |
||
792 | $this->fieldClean($newName); |
||
793 | |||
794 | if ($oldName != $newName) { |
||
795 | $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\""; |
||
796 | |||
797 | return $this->execute($sql); |
||
798 | } |
||
799 | |||
800 | return 0; |
||
801 | } |
||
802 | |||
803 | /** |
||
804 | * Changes ownership of a database |
||
805 | * This can only be done by a superuser or the owner of the database |
||
806 | * |
||
807 | * @param string $dbName database to change ownership of |
||
808 | * @param string $newOwner user that will own the database |
||
809 | * @return int 0 on success |
||
810 | */ |
||
811 | public function alterDatabaseOwner($dbName, $newOwner) |
||
812 | { |
||
813 | $this->fieldClean($dbName); |
||
814 | $this->fieldClean($newOwner); |
||
815 | |||
816 | $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\""; |
||
817 | |||
818 | return $this->execute($sql); |
||
819 | } |
||
820 | |||
821 | /** |
||
822 | * Returns prepared transactions information |
||
823 | * |
||
824 | * @param $database (optional) Find only prepared transactions executed in a specific database |
||
825 | * @return A recordset |
||
826 | */ |
||
827 | public function getPreparedXacts($database = null) |
||
828 | { |
||
829 | if ($database === null) { |
||
830 | $sql = 'SELECT * FROM pg_prepared_xacts'; |
||
831 | } else { |
||
832 | $this->clean($database); |
||
833 | $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts |
||
834 | WHERE database='{$database}' ORDER BY owner"; |
||
835 | } |
||
836 | |||
837 | return $this->selectSet($sql); |
||
838 | } |
||
839 | |||
840 | /** |
||
841 | * Searches all system catalogs to find objects that match a certain name. |
||
842 | * |
||
843 | * @param $term The search term |
||
844 | * @param $filter The object type to restrict to ('' means no restriction) |
||
845 | * @return A recordset |
||
846 | */ |
||
847 | View Code Duplication | public function findObject($term, $filter) |
|
848 | { |
||
849 | $conf = $this->conf; |
||
850 | |||
851 | /*about escaping: |
||
852 | * SET standard_conforming_string is not available before 8.2 |
||
853 | * So we must use PostgreSQL specific notation :/ |
||
854 | * E'' notation is not available before 8.1 |
||
855 | * $$ is available since 8.0 |
||
856 | * Nothing specific from 7.4 |
||
857 | */ |
||
858 | |||
859 | // Escape search term for ILIKE match |
||
860 | $this->clean($term); |
||
861 | $this->clean($filter); |
||
862 | $term = str_replace('_', '\_', $term); |
||
863 | $term = str_replace('%', '\%', $term); |
||
864 | |||
865 | // Exclude system relations if necessary |
||
866 | if (!$conf['show_system']) { |
||
867 | // XXX: The mention of information_schema here is in the wrong place, but |
||
868 | // it's the quickest fix to exclude the info schema from 7.4 |
||
869 | $where = " AND pn.nspname NOT LIKE \$_PATERN_\$pg\_%\$_PATERN_\$ AND pn.nspname != 'information_schema'"; |
||
870 | $lan_where = 'AND pl.lanispl'; |
||
871 | } else { |
||
872 | $where = ''; |
||
873 | $lan_where = ''; |
||
874 | } |
||
875 | |||
876 | // Apply outer filter |
||
877 | $sql = ''; |
||
878 | if ($filter != '') { |
||
879 | $sql = 'SELECT * FROM ('; |
||
880 | } |
||
881 | |||
882 | $term = "\$_PATERN_\$%{$term}%\$_PATERN_\$"; |
||
883 | |||
884 | $sql .= " |
||
885 | SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name |
||
886 | FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE {$term} {$where} |
||
887 | UNION ALL |
||
888 | SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid, |
||
889 | pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn |
||
890 | WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE {$term} {$where} |
||
891 | UNION ALL |
||
892 | SELECT CASE WHEN pc.relkind='r' THEN 'COLUMNTABLE' ELSE 'COLUMNVIEW' END, NULL, pn.nspname, pc.relname, pa.attname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn, |
||
893 | pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid |
||
894 | AND pa.attname ILIKE {$term} AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where} |
||
895 | UNION ALL |
||
896 | SELECT 'FUNCTION', pp.oid, pn.nspname, NULL, pp.proname || '(' || pg_catalog.oidvectortypes(pp.proargtypes) || ')' FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pn |
||
897 | WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE {$term} {$where} |
||
898 | UNION ALL |
||
899 | SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn, |
||
900 | pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid |
||
901 | AND pi.indexrelid=pc2.oid |
||
902 | AND NOT EXISTS ( |
||
903 | SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c |
||
904 | ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) |
||
905 | WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p') |
||
906 | ) |
||
907 | AND pc2.relname ILIKE {$term} {$where} |
||
908 | UNION ALL |
||
909 | SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn, |
||
910 | pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0 |
||
911 | AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS ( |
||
912 | SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c |
||
913 | ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) |
||
914 | WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p') |
||
915 | ) END |
||
916 | AND pc2.conname ILIKE {$term} {$where} |
||
917 | UNION ALL |
||
918 | SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn, |
||
919 | pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0 |
||
920 | AND pc.conname ILIKE {$term} {$where} |
||
921 | UNION ALL |
||
922 | SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn, |
||
923 | pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid |
||
924 | AND ( pt.tgconstraint = 0 OR NOT EXISTS |
||
925 | (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c |
||
926 | ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) |
||
927 | WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f')) |
||
928 | AND pt.tgname ILIKE {$term} {$where} |
||
929 | UNION ALL |
||
930 | SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r |
||
931 | JOIN pg_catalog.pg_class c ON c.oid = r.ev_class |
||
932 | LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace |
||
933 | WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where} |
||
934 | UNION ALL |
||
935 | SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r |
||
936 | JOIN pg_catalog.pg_class c ON c.oid = r.ev_class |
||
937 | LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace |
||
938 | WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where} |
||
939 | "; |
||
940 | |||
941 | // Add advanced objects if show_advanced is set |
||
942 | if ($conf['show_advanced']) { |
||
943 | $sql .= " |
||
944 | UNION ALL |
||
945 | SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL, |
||
946 | pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn |
||
947 | WHERE pt.typnamespace=pn.oid AND typname ILIKE {$term} |
||
948 | AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid)) |
||
949 | {$where} |
||
950 | UNION ALL |
||
951 | SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn |
||
952 | WHERE po.oprnamespace=pn.oid AND oprname ILIKE {$term} {$where} |
||
953 | UNION ALL |
||
954 | SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc, |
||
955 | pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE {$term} {$where} |
||
956 | UNION ALL |
||
957 | SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl |
||
958 | WHERE lanname ILIKE {$term} {$lan_where} |
||
959 | UNION ALL |
||
960 | SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p |
||
961 | LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid |
||
962 | WHERE p.proisagg AND p.proname ILIKE {$term} {$where} |
||
963 | UNION ALL |
||
964 | SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po, |
||
965 | pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid |
||
966 | AND po.opcname ILIKE {$term} {$where} |
||
967 | "; |
||
968 | } // Otherwise just add domains |
||
969 | else { |
||
970 | $sql .= " |
||
971 | UNION ALL |
||
972 | SELECT 'DOMAIN', pt.oid, pn.nspname, NULL, |
||
973 | pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn |
||
974 | WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE {$term} |
||
975 | AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid)) |
||
976 | {$where} |
||
977 | "; |
||
978 | } |
||
979 | |||
980 | if ($filter != '') { |
||
981 | // We use like to make RULE, CONSTRAINT and COLUMN searches work |
||
982 | $sql .= ") AS sub WHERE type LIKE '{$filter}%' "; |
||
983 | } |
||
984 | |||
985 | $sql .= 'ORDER BY type, schemaname, relname, name'; |
||
986 | |||
987 | return $this->selectSet($sql); |
||
988 | } |
||
989 | |||
990 | /** |
||
991 | * Returns all available variable information. |
||
992 | * |
||
993 | * @return A recordset |
||
994 | */ |
||
995 | public function getVariables() |
||
996 | { |
||
997 | $sql = 'SHOW ALL'; |
||
998 | |||
999 | return $this->selectSet($sql); |
||
1000 | } |
||
1001 | |||
1002 | // Schema functons |
||
1003 | |||
1004 | /** |
||
1005 | * Return all schemas in the current database. |
||
1006 | * |
||
1007 | * @return All schemas, sorted alphabetically |
||
1008 | */ |
||
1009 | View Code Duplication | public function getSchemas() |
|
1010 | { |
||
1011 | $conf = $this->conf; |
||
1012 | |||
1013 | if (!$conf['show_system']) { |
||
1014 | $where = "WHERE nspname NOT LIKE 'pg@_%' ESCAPE '@' AND nspname != 'information_schema'"; |
||
1015 | } else { |
||
1016 | $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'"; |
||
1017 | } |
||
1018 | |||
1019 | $sql = " |
||
1020 | SELECT pn.nspname, pu.rolname AS nspowner, |
||
1021 | pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment |
||
1022 | FROM pg_catalog.pg_namespace pn |
||
1023 | LEFT JOIN pg_catalog.pg_roles pu ON (pn.nspowner = pu.oid) |
||
1024 | {$where} |
||
1025 | ORDER BY nspname"; |
||
1026 | |||
1027 | return $this->selectSet($sql); |
||
1028 | } |
||
1029 | |||
1030 | /** |
||
1031 | * Sets the current working schema. Will also set Class variable. |
||
1032 | * |
||
1033 | * @param $schema The the name of the schema to work in |
||
1034 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
1035 | */ |
||
1036 | public function setSchema($schema) |
||
1037 | { |
||
1038 | // Get the current schema search path, including 'pg_catalog'. |
||
1039 | $search_path = $this->getSearchPath(); |
||
1040 | // Prepend $schema to search path |
||
1041 | array_unshift($search_path, $schema); |
||
1042 | $status = $this->setSearchPath($search_path); |
||
1043 | if ($status == 0) { |
||
1044 | $this->_schema = $schema; |
||
1045 | |||
1046 | return 0; |
||
1047 | } |
||
1048 | |||
1049 | return $status; |
||
1050 | } |
||
1051 | |||
1052 | /** |
||
1053 | * Return the current schema search path |
||
1054 | * |
||
1055 | * @return Array of schema names |
||
1056 | */ |
||
1057 | public function getSearchPath() |
||
1058 | { |
||
1059 | $sql = 'SELECT current_schemas(false) AS search_path'; |
||
1060 | |||
1061 | return $this->phpArray($this->selectField($sql, 'search_path')); |
||
1062 | } |
||
1063 | |||
1064 | /** |
||
1065 | * Sets the current schema search path |
||
1066 | * |
||
1067 | * @param $paths An array of schemas in required search order |
||
1068 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
1069 | */ |
||
1070 | public function setSearchPath($paths) |
||
1071 | { |
||
1072 | if (!is_array($paths)) { |
||
1073 | return -1; |
||
1074 | } |
||
1075 | |||
1076 | if (sizeof($paths) == 0) { |
||
1077 | return -2; |
||
1078 | } elseif (sizeof($paths) == 1 && $paths[0] == '') { |
||
1079 | // Need to handle empty paths in some cases |
||
1080 | $paths[0] = 'pg_catalog'; |
||
1081 | } |
||
1082 | |||
1083 | // Loop over all the paths to check that none are empty |
||
1084 | $temp = []; |
||
1085 | foreach ($paths as $schema) { |
||
1086 | if ($schema != '') { |
||
1087 | $temp[] = $schema; |
||
1088 | } |
||
1089 | } |
||
1090 | $this->fieldArrayClean($temp); |
||
1091 | |||
1092 | $sql = 'SET SEARCH_PATH TO "' . implode('","', $temp) . '"'; |
||
1093 | |||
1094 | return $this->execute($sql); |
||
1095 | } |
||
1096 | |||
1097 | /** |
||
1098 | * Cleans (escapes) an array of field names |
||
1099 | * |
||
1100 | * @param $arr The array to clean, by reference |
||
1101 | * @return The cleaned array |
||
1102 | */ |
||
1103 | public function fieldArrayClean(&$arr) |
||
1104 | { |
||
1105 | foreach ($arr as $k => $v) { |
||
1106 | if ($v === null) { |
||
1107 | continue; |
||
1108 | } |
||
1109 | |||
1110 | $arr[$k] = str_replace('"', '""', $v); |
||
1111 | } |
||
1112 | |||
1113 | return $arr; |
||
1114 | } |
||
1115 | |||
1116 | /** |
||
1117 | * Creates a new schema. |
||
1118 | * |
||
1119 | * @param $schemaname The name of the schema to create |
||
1120 | * @param string $authorization (optional) The username to create the schema for. |
||
1121 | * @param string $comment (optional) If omitted, defaults to nothing |
||
1122 | * @return bool|int 0 success |
||
1123 | */ |
||
1124 | public function createSchema($schemaname, $authorization = '', $comment = '') |
||
1125 | { |
||
1126 | $this->fieldClean($schemaname); |
||
1127 | $this->fieldClean($authorization); |
||
1128 | |||
1129 | $sql = "CREATE SCHEMA \"{$schemaname}\""; |
||
1130 | if ($authorization != '') { |
||
1131 | $sql .= " AUTHORIZATION \"{$authorization}\""; |
||
1132 | } |
||
1133 | |||
1134 | if ($comment != '') { |
||
1135 | $status = $this->beginTransaction(); |
||
1136 | if ($status != 0) { |
||
1137 | return -1; |
||
1138 | } |
||
1139 | } |
||
1140 | |||
1141 | // Create the new schema |
||
1142 | $status = $this->execute($sql); |
||
1143 | if ($status != 0) { |
||
1144 | $this->rollbackTransaction(); |
||
1145 | |||
1146 | return -1; |
||
1147 | } |
||
1148 | |||
1149 | // Set the comment |
||
1150 | View Code Duplication | if ($comment != '') { |
|
1151 | $status = $this->setComment('SCHEMA', $schemaname, '', $comment); |
||
1152 | if ($status != 0) { |
||
1153 | $this->rollbackTransaction(); |
||
1154 | |||
1155 | return -1; |
||
1156 | } |
||
1157 | |||
1158 | return $this->endTransaction(); |
||
1159 | } |
||
1160 | |||
1161 | return 0; |
||
1162 | } |
||
1163 | |||
1164 | /** |
||
1165 | * Updates a schema. |
||
1166 | * |
||
1167 | * @param $schemaname The name of the schema to drop |
||
1168 | * @param $comment The new comment for this schema |
||
1169 | * @param $name |
||
1170 | * @param $owner The new owner for this schema |
||
1171 | * @return bool|int 0 success |
||
1172 | */ |
||
1173 | public function updateSchema($schemaname, $comment, $name, $owner) |
||
1174 | { |
||
1175 | $this->fieldClean($schemaname); |
||
1176 | $this->fieldClean($name); |
||
1177 | $this->fieldClean($owner); |
||
1178 | |||
1179 | $status = $this->beginTransaction(); |
||
1180 | if ($status != 0) { |
||
1181 | $this->rollbackTransaction(); |
||
1182 | |||
1183 | return -1; |
||
1184 | } |
||
1185 | |||
1186 | $status = $this->setComment('SCHEMA', $schemaname, '', $comment); |
||
1187 | if ($status != 0) { |
||
1188 | $this->rollbackTransaction(); |
||
1189 | |||
1190 | return -1; |
||
1191 | } |
||
1192 | |||
1193 | $schema_rs = $this->getSchemaByName($schemaname); |
||
1194 | /* Only if the owner change */ |
||
1195 | View Code Duplication | if ($schema_rs->fields['ownername'] != $owner) { |
|
1196 | $sql = "ALTER SCHEMA \"{$schemaname}\" OWNER TO \"{$owner}\""; |
||
1197 | $status = $this->execute($sql); |
||
1198 | if ($status != 0) { |
||
1199 | $this->rollbackTransaction(); |
||
1200 | |||
1201 | return -1; |
||
1202 | } |
||
1203 | } |
||
1204 | |||
1205 | // Only if the name has changed |
||
1206 | View Code Duplication | if ($name != $schemaname) { |
|
1207 | $sql = "ALTER SCHEMA \"{$schemaname}\" RENAME TO \"{$name}\""; |
||
1208 | $status = $this->execute($sql); |
||
1209 | if ($status != 0) { |
||
1210 | $this->rollbackTransaction(); |
||
1211 | |||
1212 | return -1; |
||
1213 | } |
||
1214 | } |
||
1215 | |||
1216 | return $this->endTransaction(); |
||
1217 | } |
||
1218 | |||
1219 | /** |
||
1220 | * Return all information relating to a schema |
||
1221 | * |
||
1222 | * @param $schema The name of the schema |
||
1223 | * @return Schema information |
||
1224 | */ |
||
1225 | public function getSchemaByName($schema) |
||
1226 | { |
||
1227 | $this->clean($schema); |
||
1228 | $sql = " |
||
1229 | SELECT nspname, nspowner, r.rolname AS ownername, nspacl, |
||
1230 | pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment |
||
1231 | FROM pg_catalog.pg_namespace pn |
||
1232 | LEFT JOIN pg_roles as r ON pn.nspowner = r.oid |
||
1233 | WHERE nspname='{$schema}'"; |
||
1234 | |||
1235 | return $this->selectSet($sql); |
||
1236 | } |
||
1237 | |||
1238 | // Table functions |
||
1239 | |||
1240 | /** |
||
1241 | * Drops a schema. |
||
1242 | * |
||
1243 | * @param $schemaname The name of the schema to drop |
||
1244 | * @param $cascade True to cascade drop, false to restrict |
||
1245 | * @return \PHPPgAdmin\Database\A 0 success |
||
1246 | */ |
||
1247 | public function dropSchema($schemaname, $cascade) |
||
1248 | { |
||
1249 | $this->fieldClean($schemaname); |
||
1250 | |||
1251 | $sql = "DROP SCHEMA \"{$schemaname}\""; |
||
1252 | if ($cascade) { |
||
1253 | $sql .= ' CASCADE'; |
||
1254 | } |
||
1255 | |||
1256 | return $this->execute($sql); |
||
1257 | } |
||
1258 | |||
1259 | /** |
||
1260 | * Return all tables in current database (and schema) |
||
1261 | * |
||
1262 | * @param bool|True $all True to fetch all tables, false for just in current schema |
||
1263 | * @return \PHPPgAdmin\Database\All tables, sorted alphabetically |
||
1264 | */ |
||
1265 | public function getTables($all = false) |
||
1266 | { |
||
1267 | $c_schema = $this->_schema; |
||
1268 | $this->clean($c_schema); |
||
1269 | if ($all) { |
||
1270 | // Exclude pg_catalog and information_schema tables |
||
1271 | $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner |
||
1272 | FROM pg_catalog.pg_tables |
||
1273 | WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') |
||
1274 | ORDER BY schemaname, tablename"; |
||
1275 | } else { |
||
1276 | $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, |
||
1277 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment, |
||
1278 | reltuples::bigint, |
||
1279 | (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace |
||
1280 | FROM pg_catalog.pg_class c |
||
1281 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace |
||
1282 | WHERE c.relkind = 'r' |
||
1283 | AND nspname='{$c_schema}' |
||
1284 | ORDER BY c.relname"; |
||
1285 | } |
||
1286 | |||
1287 | return $this->selectSet($sql); |
||
1288 | } |
||
1289 | |||
1290 | /** |
||
1291 | * Finds the names and schemas of parent tables (in order) |
||
1292 | * |
||
1293 | * @param $table The table to find the parents for |
||
1294 | * @return A recordset |
||
1295 | */ |
||
1296 | View Code Duplication | public function getTableParents($table) |
|
1297 | { |
||
1298 | $c_schema = $this->_schema; |
||
1299 | $this->clean($c_schema); |
||
1300 | $this->clean($table); |
||
1301 | |||
1302 | $sql = " |
||
1303 | SELECT |
||
1304 | pn.nspname, relname |
||
1305 | FROM |
||
1306 | pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn |
||
1307 | WHERE |
||
1308 | pc.oid=pi.inhparent |
||
1309 | AND pc.relnamespace=pn.oid |
||
1310 | AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}' |
||
1311 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}')) |
||
1312 | ORDER BY |
||
1313 | pi.inhseqno |
||
1314 | "; |
||
1315 | |||
1316 | return $this->selectSet($sql); |
||
1317 | } |
||
1318 | |||
1319 | /** |
||
1320 | * Finds the names and schemas of child tables |
||
1321 | * |
||
1322 | * @param $table The table to find the children for |
||
1323 | * @return A recordset |
||
1324 | */ |
||
1325 | View Code Duplication | public function getTableChildren($table) |
|
1326 | { |
||
1327 | $c_schema = $this->_schema; |
||
1328 | $this->clean($c_schema); |
||
1329 | $this->clean($table); |
||
1330 | |||
1331 | $sql = " |
||
1332 | SELECT |
||
1333 | pn.nspname, relname |
||
1334 | FROM |
||
1335 | pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn |
||
1336 | WHERE |
||
1337 | pc.oid=pi.inhrelid |
||
1338 | AND pc.relnamespace=pn.oid |
||
1339 | AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}' |
||
1340 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}')) |
||
1341 | "; |
||
1342 | |||
1343 | return $this->selectSet($sql); |
||
1344 | } |
||
1345 | |||
1346 | /** |
||
1347 | * Returns the SQL definition for the table. |
||
1348 | * |
||
1349 | * @pre MUST be run within a transaction |
||
1350 | * @param $table The table to define |
||
1351 | * @param bool|True $clean True to issue drop command, false otherwise |
||
1352 | * @return \PHPPgAdmin\Database\A string containing the formatted SQL code |
||
1353 | */ |
||
1354 | public function getTableDefPrefix($table, $clean = false) |
||
1355 | { |
||
1356 | // Fetch table |
||
1357 | $t = $this->getTable($table); |
||
1358 | if (!is_object($t) || $t->recordCount() != 1) { |
||
1359 | $this->rollbackTransaction(); |
||
1360 | |||
1361 | return null; |
||
1362 | } |
||
1363 | $this->fieldClean($t->fields['relname']); |
||
1364 | $this->fieldClean($t->fields['nspname']); |
||
1365 | |||
1366 | // Fetch attributes |
||
1367 | $atts = $this->getTableAttributes($table); |
||
1368 | if (!is_object($atts)) { |
||
1369 | $this->rollbackTransaction(); |
||
1370 | |||
1371 | return null; |
||
1372 | } |
||
1373 | |||
1374 | // Fetch constraints |
||
1375 | $cons = $this->getConstraints($table); |
||
1376 | if (!is_object($cons)) { |
||
1377 | $this->rollbackTransaction(); |
||
1378 | |||
1379 | return null; |
||
1380 | } |
||
1381 | |||
1382 | // Output a reconnect command to create the table as the correct user |
||
1383 | $sql = $this->getChangeUserSQL($t->fields['relowner']) . "\n\n"; |
||
1384 | |||
1385 | // Set schema search path |
||
1386 | $sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n"; |
||
1387 | |||
1388 | // Begin CREATE TABLE definition |
||
1389 | $sql .= "-- Definition\n\n"; |
||
1390 | // DROP TABLE must be fully qualified in case a table with the same name exists |
||
1391 | // in pg_catalog. |
||
1392 | if (!$clean) { |
||
1393 | $sql .= '-- '; |
||
1394 | } |
||
1395 | |||
1396 | $sql .= 'DROP TABLE '; |
||
1397 | $sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n"; |
||
1398 | $sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n"; |
||
1399 | |||
1400 | // Output all table columns |
||
1401 | $col_comments_sql = ''; // Accumulate comments on columns |
||
1402 | $num = $atts->recordCount() + $cons->recordCount(); |
||
1403 | $i = 1; |
||
1404 | while (!$atts->EOF) { |
||
1405 | $this->fieldClean($atts->fields['attname']); |
||
1406 | $sql .= " \"{$atts->fields['attname']}\""; |
||
1407 | // Dump SERIAL and BIGSERIAL columns correctly |
||
1408 | if ($this->phpBool($atts->fields['attisserial']) && |
||
1409 | ($atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) { |
||
1410 | if ($atts->fields['type'] == 'integer') { |
||
1411 | $sql .= ' SERIAL'; |
||
1412 | } else { |
||
1413 | $sql .= ' BIGSERIAL'; |
||
1414 | } |
||
1415 | } else { |
||
1416 | $sql .= ' ' . $this->formatType($atts->fields['type'], $atts->fields['atttypmod']); |
||
1417 | |||
1418 | // Add NOT NULL if necessary |
||
1419 | if ($this->phpBool($atts->fields['attnotnull'])) { |
||
1420 | $sql .= ' NOT NULL'; |
||
1421 | } |
||
1422 | |||
1423 | // Add default if necessary |
||
1424 | if ($atts->fields['adsrc'] !== null) { |
||
1425 | $sql .= " DEFAULT {$atts->fields['adsrc']}"; |
||
1426 | } |
||
1427 | } |
||
1428 | |||
1429 | // Output comma or not |
||
1430 | if ($i < $num) { |
||
1431 | $sql .= ",\n"; |
||
1432 | } else { |
||
1433 | $sql .= "\n"; |
||
1434 | } |
||
1435 | |||
1436 | // Does this column have a comment? |
||
1437 | View Code Duplication | if ($atts->fields['comment'] !== null) { |
|
1438 | $this->clean($atts->fields['comment']); |
||
1439 | $col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\" IS '{$atts->fields['comment']}';\n"; |
||
1440 | } |
||
1441 | |||
1442 | $atts->moveNext(); |
||
1443 | $i++; |
||
1444 | } |
||
1445 | // Output all table constraints |
||
1446 | while (!$cons->EOF) { |
||
1447 | $this->fieldClean($cons->fields['conname']); |
||
1448 | $sql .= " CONSTRAINT \"{$cons->fields['conname']}\" "; |
||
1449 | // Nasty hack to support pre-7.4 PostgreSQL |
||
1450 | if ($cons->fields['consrc'] !== null) { |
||
1451 | $sql .= $cons->fields['consrc']; |
||
1452 | } else { |
||
1453 | switch ($cons->fields['contype']) { |
||
1454 | View Code Duplication | case 'p': |
|
1455 | $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey'])); |
||
1456 | $sql .= 'PRIMARY KEY (' . join(',', $keys) . ')'; |
||
1457 | break; |
||
1458 | View Code Duplication | case 'u': |
|
1459 | $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey'])); |
||
1460 | $sql .= 'UNIQUE (' . join(',', $keys) . ')'; |
||
1461 | break; |
||
1462 | default: |
||
1463 | // Unrecognised constraint |
||
1464 | $this->rollbackTransaction(); |
||
1465 | |||
1466 | return null; |
||
1467 | } |
||
1468 | } |
||
1469 | |||
1470 | // Output comma or not |
||
1471 | if ($i < $num) { |
||
1472 | $sql .= ",\n"; |
||
1473 | } else { |
||
1474 | $sql .= "\n"; |
||
1475 | } |
||
1476 | |||
1477 | $cons->moveNext(); |
||
1478 | $i++; |
||
1479 | } |
||
1480 | |||
1481 | $sql .= ')'; |
||
1482 | |||
1483 | // @@@@ DUMP CLUSTERING INFORMATION |
||
1484 | |||
1485 | // Inherits |
||
1486 | /* |
||
1487 | * XXX: This is currently commented out as handling inheritance isn't this simple. |
||
1488 | * You also need to make sure you don't dump inherited columns and defaults, as well |
||
1489 | * as inherited NOT NULL and CHECK constraints. So for the time being, we just do |
||
1490 | * not claim to support inheritance. |
||
1491 | $parents = $this->getTableParents($table); |
||
1492 | if ($parents->recordCount() > 0) { |
||
1493 | $sql .= " INHERITS ("; |
||
1494 | while (!$parents->EOF) { |
||
1495 | $this->fieldClean($parents->fields['relname']); |
||
1496 | // Qualify the parent table if it's in another schema |
||
1497 | if ($parents->fields['schemaname'] != $this->_schema) { |
||
1498 | $this->fieldClean($parents->fields['schemaname']); |
||
1499 | $sql .= "\"{$parents->fields['schemaname']}\"."; |
||
1500 | } |
||
1501 | $sql .= "\"{$parents->fields['relname']}\""; |
||
1502 | |||
1503 | $parents->moveNext(); |
||
1504 | if (!$parents->EOF) $sql .= ', '; |
||
1505 | } |
||
1506 | $sql .= ")"; |
||
1507 | } |
||
1508 | */ |
||
1509 | |||
1510 | // Handle WITHOUT OIDS |
||
1511 | if ($this->hasObjectID($table)) { |
||
1512 | $sql .= ' WITH OIDS'; |
||
1513 | } else { |
||
1514 | $sql .= ' WITHOUT OIDS'; |
||
1515 | } |
||
1516 | |||
1517 | $sql .= ";\n"; |
||
1518 | |||
1519 | // Column storage and statistics |
||
1520 | $atts->moveFirst(); |
||
1521 | $first = true; |
||
1522 | while (!$atts->EOF) { |
||
1523 | $this->fieldClean($atts->fields['attname']); |
||
1524 | // Statistics first |
||
1525 | if ($atts->fields['attstattarget'] >= 0) { |
||
1526 | if ($first) { |
||
1527 | $sql .= "\n"; |
||
1528 | $first = false; |
||
1529 | } |
||
1530 | $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n"; |
||
1531 | } |
||
1532 | // Then storage |
||
1533 | if ($atts->fields['attstorage'] != $atts->fields['typstorage']) { |
||
1534 | switch ($atts->fields['attstorage']) { |
||
1535 | case 'p': |
||
1536 | $storage = 'PLAIN'; |
||
1537 | break; |
||
1538 | case 'e': |
||
1539 | $storage = 'EXTERNAL'; |
||
1540 | break; |
||
1541 | case 'm': |
||
1542 | $storage = 'MAIN'; |
||
1543 | break; |
||
1544 | case 'x': |
||
1545 | $storage = 'EXTENDED'; |
||
1546 | break; |
||
1547 | default: |
||
1548 | // Unknown storage type |
||
1549 | $this->rollbackTransaction(); |
||
1550 | |||
1551 | return null; |
||
1552 | } |
||
1553 | $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n"; |
||
1554 | } |
||
1555 | |||
1556 | $atts->moveNext(); |
||
1557 | } |
||
1558 | |||
1559 | // Comment |
||
1560 | View Code Duplication | if ($t->fields['relcomment'] !== null) { |
|
1561 | $this->clean($t->fields['relcomment']); |
||
1562 | $sql .= "\n-- Comment\n\n"; |
||
1563 | $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n"; |
||
1564 | } |
||
1565 | |||
1566 | // Add comments on columns, if any |
||
1567 | if ($col_comments_sql != '') { |
||
1568 | $sql .= $col_comments_sql; |
||
1569 | } |
||
1570 | |||
1571 | // Privileges |
||
1572 | $privs = $this->getPrivileges($table, 'table'); |
||
1573 | if (!is_array($privs)) { |
||
1574 | $this->rollbackTransaction(); |
||
1575 | |||
1576 | return null; |
||
1577 | } |
||
1578 | |||
1579 | if (sizeof($privs) > 0) { |
||
1580 | $sql .= "\n-- Privileges\n\n"; |
||
1581 | /* |
||
1582 | * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to |
||
1583 | * wire-in knowledge about the default public privileges for different |
||
1584 | * kinds of objects. |
||
1585 | */ |
||
1586 | $sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n"; |
||
1587 | foreach ($privs as $v) { |
||
1588 | // Get non-GRANT OPTION privs |
||
1589 | $nongrant = array_diff($v[2], $v[4]); |
||
1590 | |||
1591 | // Skip empty or owner ACEs |
||
1592 | if (sizeof($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) { |
||
1593 | continue; |
||
1594 | } |
||
1595 | |||
1596 | // Change user if necessary |
||
1597 | View Code Duplication | if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) { |
|
1598 | $grantor = $v[3]; |
||
1599 | $this->clean($grantor); |
||
1600 | $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n"; |
||
1601 | } |
||
1602 | |||
1603 | // Output privileges with no GRANT OPTION |
||
1604 | $sql .= 'GRANT ' . join(', ', $nongrant) . " ON TABLE \"{$t->fields['relname']}\" TO "; |
||
1605 | View Code Duplication | switch ($v[0]) { |
|
1606 | case 'public': |
||
1607 | $sql .= "PUBLIC;\n"; |
||
1608 | break; |
||
1609 | case 'user': |
||
1610 | $this->fieldClean($v[1]); |
||
1611 | $sql .= "\"{$v[1]}\";\n"; |
||
1612 | break; |
||
1613 | case 'group': |
||
1614 | $this->fieldClean($v[1]); |
||
1615 | $sql .= "GROUP \"{$v[1]}\";\n"; |
||
1616 | break; |
||
1617 | default: |
||
1618 | // Unknown privilege type - fail |
||
1619 | $this->rollbackTransaction(); |
||
1620 | |||
1621 | return null; |
||
1622 | } |
||
1623 | |||
1624 | // Reset user if necessary |
||
1625 | if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) { |
||
1626 | $sql .= "RESET SESSION AUTHORIZATION;\n"; |
||
1627 | } |
||
1628 | |||
1629 | // Output privileges with GRANT OPTION |
||
1630 | |||
1631 | // Skip empty or owner ACEs |
||
1632 | if (!$this->hasGrantOption() || sizeof($v[4]) == 0) { |
||
1633 | continue; |
||
1634 | } |
||
1635 | |||
1636 | // Change user if necessary |
||
1637 | View Code Duplication | if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) { |
|
1638 | $grantor = $v[3]; |
||
1639 | $this->clean($grantor); |
||
1640 | $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n"; |
||
1641 | } |
||
1642 | |||
1643 | $sql .= 'GRANT ' . join(', ', $v[4]) . " ON \"{$t->fields['relname']}\" TO "; |
||
1644 | View Code Duplication | switch ($v[0]) { |
|
1645 | case 'public': |
||
1646 | $sql .= 'PUBLIC'; |
||
1647 | break; |
||
1648 | case 'user': |
||
1649 | $this->fieldClean($v[1]); |
||
1650 | $sql .= "\"{$v[1]}\""; |
||
1651 | break; |
||
1652 | case 'group': |
||
1653 | $this->fieldClean($v[1]); |
||
1654 | $sql .= "GROUP \"{$v[1]}\""; |
||
1655 | break; |
||
1656 | default: |
||
1657 | // Unknown privilege type - fail |
||
1658 | return null; |
||
1659 | } |
||
1660 | $sql .= " WITH GRANT OPTION;\n"; |
||
1661 | |||
1662 | // Reset user if necessary |
||
1663 | if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) { |
||
1664 | $sql .= "RESET SESSION AUTHORIZATION;\n"; |
||
1665 | } |
||
1666 | } |
||
1667 | } |
||
1668 | |||
1669 | // Add a newline to separate data that follows (if any) |
||
1670 | $sql .= "\n"; |
||
1671 | |||
1672 | return $sql; |
||
1673 | } |
||
1674 | |||
1675 | /** |
||
1676 | * Returns table information |
||
1677 | * |
||
1678 | * @param $table The name of the table |
||
1679 | * @return A recordset |
||
1680 | */ |
||
1681 | View Code Duplication | public function getTable($table) |
|
1682 | { |
||
1683 | $c_schema = $this->_schema; |
||
1684 | $this->clean($c_schema); |
||
1685 | $this->clean($table); |
||
1686 | |||
1687 | $sql = " |
||
1688 | SELECT |
||
1689 | c.relname, n.nspname, u.usename AS relowner, |
||
1690 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment, |
||
1691 | (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace |
||
1692 | FROM pg_catalog.pg_class c |
||
1693 | LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner |
||
1694 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace |
||
1695 | WHERE c.relkind = 'r' |
||
1696 | AND n.nspname = '{$c_schema}' |
||
1697 | AND n.oid = c.relnamespace |
||
1698 | AND c.relname = '{$table}'"; |
||
1699 | |||
1700 | return $this->selectSet($sql); |
||
1701 | } |
||
1702 | |||
1703 | /** |
||
1704 | * Retrieve the attribute definition of a table |
||
1705 | * |
||
1706 | * @param $table The name of the table |
||
1707 | * @param $field (optional) The name of a field to return |
||
1708 | * @return All attributes in order |
||
1709 | */ |
||
1710 | public function getTableAttributes($table, $field = '') |
||
1711 | { |
||
1712 | $c_schema = $this->_schema; |
||
1713 | $this->clean($c_schema); |
||
1714 | $this->clean($table); |
||
1715 | $this->clean($field); |
||
1716 | |||
1717 | if ($field == '') { |
||
1718 | // This query is made much more complex by the addition of the 'attisserial' field. |
||
1719 | // The subquery to get that field checks to see if there is an internally dependent |
||
1720 | // sequence on the field. |
||
1721 | $sql = " |
||
1722 | SELECT |
||
1723 | a.attname, a.attnum, |
||
1724 | pg_catalog.format_type(a.atttypid, a.atttypmod) as type, |
||
1725 | a.atttypmod, |
||
1726 | a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc, |
||
1727 | a.attstattarget, a.attstorage, t.typstorage, |
||
1728 | ( |
||
1729 | SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc |
||
1730 | WHERE pd.objid=pc.oid |
||
1731 | AND pd.classid=pc.tableoid |
||
1732 | AND pd.refclassid=pc.tableoid |
||
1733 | AND pd.refobjid=a.attrelid |
||
1734 | AND pd.refobjsubid=a.attnum |
||
1735 | AND pd.deptype='i' |
||
1736 | AND pc.relkind='S' |
||
1737 | ) IS NOT NULL AS attisserial, |
||
1738 | pg_catalog.col_description(a.attrelid, a.attnum) AS comment |
||
1739 | FROM |
||
1740 | pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef |
||
1741 | ON a.attrelid=adef.adrelid |
||
1742 | AND a.attnum=adef.adnum |
||
1743 | LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid |
||
1744 | WHERE |
||
1745 | a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
1746 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE |
||
1747 | nspname = '{$c_schema}')) |
||
1748 | AND a.attnum > 0 AND NOT a.attisdropped |
||
1749 | ORDER BY a.attnum"; |
||
1750 | } else { |
||
1751 | $sql = " |
||
1752 | SELECT |
||
1753 | a.attname, a.attnum, |
||
1754 | pg_catalog.format_type(a.atttypid, a.atttypmod) as type, |
||
1755 | pg_catalog.format_type(a.atttypid, NULL) as base_type, |
||
1756 | a.atttypmod, |
||
1757 | a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc, |
||
1758 | a.attstattarget, a.attstorage, t.typstorage, |
||
1759 | pg_catalog.col_description(a.attrelid, a.attnum) AS comment |
||
1760 | FROM |
||
1761 | pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef |
||
1762 | ON a.attrelid=adef.adrelid |
||
1763 | AND a.attnum=adef.adnum |
||
1764 | LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid |
||
1765 | WHERE |
||
1766 | a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
1767 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE |
||
1768 | nspname = '{$c_schema}')) |
||
1769 | AND a.attname = '{$field}'"; |
||
1770 | } |
||
1771 | |||
1772 | return $this->selectSet($sql); |
||
1773 | } |
||
1774 | |||
1775 | /** |
||
1776 | * Returns a list of all constraints on a table |
||
1777 | * |
||
1778 | * @param $table The table to find rules for |
||
1779 | * @return A recordset |
||
1780 | */ |
||
1781 | View Code Duplication | public function getConstraints($table) |
|
1782 | { |
||
1783 | $c_schema = $this->_schema; |
||
1784 | $this->clean($c_schema); |
||
1785 | $this->clean($table); |
||
1786 | |||
1787 | // This SQL is greatly complicated by the need to retrieve |
||
1788 | // index clustering information for primary and unique constraints |
||
1789 | $sql = "SELECT |
||
1790 | pc.conname, |
||
1791 | pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc, |
||
1792 | pc.contype, |
||
1793 | CASE WHEN pc.contype='u' OR pc.contype='p' THEN ( |
||
1794 | SELECT |
||
1795 | indisclustered |
||
1796 | FROM |
||
1797 | pg_catalog.pg_depend pd, |
||
1798 | pg_catalog.pg_class pl, |
||
1799 | pg_catalog.pg_index pi |
||
1800 | WHERE |
||
1801 | pd.refclassid=pc.tableoid |
||
1802 | AND pd.refobjid=pc.oid |
||
1803 | AND pd.objid=pl.oid |
||
1804 | AND pl.oid=pi.indexrelid |
||
1805 | ) ELSE |
||
1806 | NULL |
||
1807 | END AS indisclustered |
||
1808 | FROM |
||
1809 | pg_catalog.pg_constraint pc |
||
1810 | WHERE |
||
1811 | pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
1812 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||
1813 | WHERE nspname='{$c_schema}')) |
||
1814 | ORDER BY |
||
1815 | 1 |
||
1816 | "; |
||
1817 | |||
1818 | return $this->selectSet($sql); |
||
1819 | } |
||
1820 | |||
1821 | /** |
||
1822 | * Returns the SQL for changing the current user |
||
1823 | * |
||
1824 | * @param $user The user to change to |
||
1825 | * @return The SQL |
||
1826 | */ |
||
1827 | public function getChangeUserSQL($user) |
||
1828 | { |
||
1829 | $this->clean($user); |
||
1830 | |||
1831 | return "SET SESSION AUTHORIZATION '{$user}';"; |
||
1832 | } |
||
1833 | |||
1834 | /** |
||
1835 | * Change a parameter from 't' or 'f' to a boolean, (others evaluate to false) |
||
1836 | * |
||
1837 | * @param $parameter the parameter |
||
1838 | * @return bool|\PHPPgAdmin\Database\the |
||
1839 | */ |
||
1840 | public function phpBool($parameter) |
||
1841 | { |
||
1842 | $parameter = ($parameter == 't'); |
||
1843 | |||
1844 | return $parameter; |
||
1845 | } |
||
1846 | |||
1847 | /** |
||
1848 | * Formats a type correctly for display. Postgres 7.0 had no 'format_type' |
||
1849 | * built-in function, and hence we need to do it manually. |
||
1850 | * |
||
1851 | * @param $typname The name of the type |
||
1852 | * @param $typmod The contents of the typmod field |
||
1853 | * @return bool|\PHPPgAdmin\Database\The|string |
||
1854 | */ |
||
1855 | public function formatType($typname, $typmod) |
||
1856 | { |
||
1857 | // This is a specific constant in the 7.0 source |
||
1858 | $varhdrsz = 4; |
||
1859 | |||
1860 | // If the first character is an underscore, it's an array type |
||
1861 | $is_array = false; |
||
1862 | if (substr($typname, 0, 1) == '_') { |
||
1863 | $is_array = true; |
||
1864 | $typname = substr($typname, 1); |
||
1865 | } |
||
1866 | |||
1867 | // Show lengths on bpchar and varchar |
||
1868 | if ($typname == 'bpchar') { |
||
1869 | $len = $typmod - $varhdrsz; |
||
1870 | $temp = 'character'; |
||
1871 | if ($len > 1) { |
||
1872 | $temp .= "({$len})"; |
||
1873 | } |
||
1874 | } elseif ($typname == 'varchar') { |
||
1875 | $temp = 'character varying'; |
||
1876 | if ($typmod != -1) { |
||
1877 | $temp .= '(' . ($typmod - $varhdrsz) . ')'; |
||
1878 | } |
||
1879 | } elseif ($typname == 'numeric') { |
||
1880 | $temp = 'numeric'; |
||
1881 | if ($typmod != -1) { |
||
1882 | $tmp_typmod = $typmod - $varhdrsz; |
||
1883 | $precision = ($tmp_typmod >> 16) & 0xffff; |
||
1884 | $scale = $tmp_typmod & 0xffff; |
||
1885 | $temp .= "({$precision}, {$scale})"; |
||
1886 | } |
||
1887 | } else { |
||
1888 | $temp = $typname; |
||
1889 | } |
||
1890 | |||
1891 | // Add array qualifier if it's an array |
||
1892 | if ($is_array) { |
||
1893 | $temp .= '[]'; |
||
1894 | } |
||
1895 | |||
1896 | return $temp; |
||
1897 | } |
||
1898 | |||
1899 | /** |
||
1900 | * Given an array of attnums and a relation, returns an array mapping |
||
1901 | * attribute number to attribute name. |
||
1902 | * |
||
1903 | * @param $table The table to get attributes for |
||
1904 | * @param $atts An array of attribute numbers |
||
1905 | * @return An array mapping attnum to attname |
||
1906 | * @return -1 $atts must be an array |
||
1907 | * @return -2 wrong number of attributes found |
||
1908 | */ |
||
1909 | public function getAttributeNames($table, $atts) |
||
1910 | { |
||
1911 | $c_schema = $this->_schema; |
||
1912 | $this->clean($c_schema); |
||
1913 | $this->clean($table); |
||
1914 | $this->arrayClean($atts); |
||
1915 | |||
1916 | if (!is_array($atts)) { |
||
1917 | return -1; |
||
1918 | } |
||
1919 | |||
1920 | if (sizeof($atts) == 0) { |
||
1921 | return []; |
||
1922 | } |
||
1923 | |||
1924 | $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE |
||
1925 | attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND |
||
1926 | relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')) |
||
1927 | AND attnum IN ('" . join("','", $atts) . "')"; |
||
1928 | |||
1929 | $rs = $this->selectSet($sql); |
||
1930 | if ($rs->recordCount() != sizeof($atts)) { |
||
1931 | return -2; |
||
1932 | } |
||
1933 | |||
1934 | $temp = []; |
||
1935 | while (!$rs->EOF) { |
||
1936 | $temp[$rs->fields['attnum']] = $rs->fields['attname']; |
||
1937 | $rs->moveNext(); |
||
1938 | } |
||
1939 | |||
1940 | return $temp; |
||
1941 | } |
||
1942 | |||
1943 | /** |
||
1944 | * Cleans (escapes) an array |
||
1945 | * |
||
1946 | * @param $arr The array to clean, by reference |
||
1947 | * @return The cleaned array |
||
1948 | */ |
||
1949 | public function arrayClean(&$arr) |
||
1950 | { |
||
1951 | foreach ($arr as $k => $v) { |
||
1952 | if ($v === null) { |
||
1953 | continue; |
||
1954 | } |
||
1955 | |||
1956 | $arr[$k] = pg_escape_string($v); |
||
1957 | } |
||
1958 | |||
1959 | return $arr; |
||
1960 | } |
||
1961 | |||
1962 | /** |
||
1963 | * Checks to see whether or not a table has a unique id column |
||
1964 | * |
||
1965 | * @param $table The table name |
||
1966 | * @return True if it has a unique id, false otherwise |
||
1967 | * @return null error |
||
1968 | **/ |
||
1969 | public function hasObjectID($table) |
||
1970 | { |
||
1971 | $c_schema = $this->_schema; |
||
1972 | $this->clean($c_schema); |
||
1973 | $this->clean($table); |
||
1974 | |||
1975 | $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
1976 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')"; |
||
1977 | |||
1978 | $rs = $this->selectSet($sql); |
||
1979 | if ($rs->recordCount() != 1) { |
||
1980 | return null; |
||
1981 | } |
||
1982 | |||
1983 | $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']); |
||
1984 | |||
1985 | return $rs->fields['relhasoids']; |
||
1986 | } |
||
1987 | |||
1988 | /** |
||
1989 | * Grabs an array of users and their privileges for an object, |
||
1990 | * given its type. |
||
1991 | * |
||
1992 | * @param $object The name of the object whose privileges are to be retrieved |
||
1993 | * @param $type The type of the object (eg. database, schema, relation, function or language) |
||
1994 | * @param $table Optional, column's table if type = column |
||
1995 | * @return Privileges array |
||
1996 | * @return -1 invalid type |
||
1997 | * @return -2 object not found |
||
1998 | * @return -3 unknown privilege type |
||
1999 | */ |
||
2000 | public function getPrivileges($object, $type, $table = null) |
||
2001 | { |
||
2002 | $c_schema = $this->_schema; |
||
2003 | $this->clean($c_schema); |
||
2004 | $this->clean($object); |
||
2005 | |||
2006 | switch ($type) { |
||
2007 | case 'column': |
||
2008 | $this->clean($table); |
||
2009 | $sql = " |
||
2010 | SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl |
||
2011 | FROM pg_catalog.pg_attribute a |
||
2012 | LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid) |
||
2013 | LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid) |
||
2014 | WHERE n.nspname='{$c_schema}' |
||
2015 | AND c.relname='{$table}' |
||
2016 | AND a.attname='{$object}'"; |
||
2017 | break; |
||
2018 | case 'table': |
||
2019 | case 'view': |
||
2020 | case 'sequence': |
||
2021 | $sql = " |
||
2022 | SELECT relacl AS acl FROM pg_catalog.pg_class |
||
2023 | WHERE relname='{$object}' |
||
2024 | AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace |
||
2025 | WHERE nspname='{$c_schema}')"; |
||
2026 | break; |
||
2027 | case 'database': |
||
2028 | $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'"; |
||
2029 | break; |
||
2030 | case 'function': |
||
2031 | // Since we fetch functions by oid, they are already constrained to |
||
2032 | // the current schema. |
||
2033 | $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'"; |
||
2034 | break; |
||
2035 | case 'language': |
||
2036 | $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'"; |
||
2037 | break; |
||
2038 | case 'schema': |
||
2039 | $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'"; |
||
2040 | break; |
||
2041 | case 'tablespace': |
||
2042 | $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'"; |
||
2043 | break; |
||
2044 | default: |
||
2045 | return -1; |
||
2046 | } |
||
2047 | |||
2048 | // Fetch the ACL for object |
||
2049 | $acl = $this->selectField($sql, 'acl'); |
||
2050 | if ($acl == -1) { |
||
2051 | return -2; |
||
2052 | } |
||
2053 | |||
2054 | if ($acl == '' || $acl == null) { |
||
2055 | return []; |
||
2056 | } else { |
||
2057 | return $this->_parseACL($acl); |
||
2058 | } |
||
2059 | } |
||
2060 | |||
2061 | /** |
||
2062 | * Internal function used for parsing ACLs |
||
2063 | * |
||
2064 | * @param $acl The ACL to parse (of type aclitem[]) |
||
2065 | * @return Privileges array |
||
2066 | */ |
||
2067 | public function _parseACL($acl) |
||
2068 | { |
||
2069 | // Take off the first and last characters (the braces) |
||
2070 | $acl = substr($acl, 1, strlen($acl) - 2); |
||
2071 | |||
2072 | // Pick out individual ACE's by carefully parsing. This is necessary in order |
||
2073 | // to cope with usernames and stuff that contain commas |
||
2074 | $aces = []; |
||
2075 | $i = $j = 0; |
||
2076 | $in_quotes = false; |
||
2077 | View Code Duplication | while ($i < strlen($acl)) { |
|
2078 | // If current char is a double quote and it's not escaped, then |
||
2079 | // enter quoted bit |
||
2080 | $char = substr($acl, $i, 1); |
||
2081 | if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\')) { |
||
2082 | $in_quotes = !$in_quotes; |
||
2083 | } elseif ($char == ',' && !$in_quotes) { |
||
2084 | // Add text so far to the array |
||
2085 | $aces[] = substr($acl, $j, $i - $j); |
||
2086 | $j = $i + 1; |
||
2087 | } |
||
2088 | $i++; |
||
2089 | } |
||
2090 | // Add final text to the array |
||
2091 | $aces[] = substr($acl, $j); |
||
2092 | |||
2093 | // Create the array to be returned |
||
2094 | $temp = []; |
||
2095 | |||
2096 | // For each ACE, generate an entry in $temp |
||
2097 | foreach ($aces as $v) { |
||
2098 | |||
2099 | // If the ACE begins with a double quote, strip them off both ends |
||
2100 | // and unescape backslashes and double quotes |
||
2101 | $unquote = false; |
||
2102 | if (strpos($v, '"') === 0) { |
||
2103 | $v = substr($v, 1, strlen($v) - 2); |
||
2104 | $v = str_replace('\\"', '"', $v); |
||
2105 | $v = str_replace('\\\\', '\\', $v); |
||
2106 | } |
||
2107 | |||
2108 | // Figure out type of ACE (public, user or group) |
||
2109 | if (strpos($v, '=') === 0) { |
||
2110 | $atype = 'public'; |
||
2111 | } else { |
||
2112 | if ($this->hasRoles()) { |
||
2113 | $atype = 'role'; |
||
2114 | } else { |
||
2115 | if (strpos($v, 'group ') === 0) { |
||
2116 | $atype = 'group'; |
||
2117 | // Tear off 'group' prefix |
||
2118 | $v = substr($v, 6); |
||
2119 | } else { |
||
2120 | $atype = 'user'; |
||
2121 | } |
||
2122 | } |
||
2123 | } |
||
2124 | |||
2125 | // Break on unquoted equals sign... |
||
2126 | $i = 0; |
||
2127 | $in_quotes = false; |
||
2128 | $entity = null; |
||
2129 | $chars = null; |
||
2130 | while ($i < strlen($v)) { |
||
2131 | // If current char is a double quote and it's not escaped, then |
||
2132 | // enter quoted bit |
||
2133 | $char = substr($v, $i, 1); |
||
2134 | $next_char = substr($v, $i + 1, 1); |
||
2135 | if ($char == '"' && ($i == 0 || $next_char != '"')) { |
||
2136 | $in_quotes = !$in_quotes; |
||
2137 | } // Skip over escaped double quotes |
||
2138 | elseif ($char == '"' && $next_char == '"') { |
||
2139 | $i++; |
||
2140 | } elseif ($char == '=' && !$in_quotes) { |
||
2141 | // Split on current equals sign |
||
2142 | $entity = substr($v, 0, $i); |
||
2143 | $chars = substr($v, $i + 1); |
||
2144 | break; |
||
2145 | } |
||
2146 | $i++; |
||
2147 | } |
||
2148 | |||
2149 | // Check for quoting on entity name, and unescape if necessary |
||
2150 | if (strpos($entity, '"') === 0) { |
||
2151 | $entity = substr($entity, 1, strlen($entity) - 2); |
||
2152 | $entity = str_replace('""', '"', $entity); |
||
2153 | } |
||
2154 | |||
2155 | // New row to be added to $temp |
||
2156 | // (type, grantee, privileges, grantor, grant option? |
||
2157 | $row = [$atype, $entity, [], '', []]; |
||
2158 | |||
2159 | // Loop over chars and add privs to $row |
||
2160 | for ($i = 0; $i < strlen($chars); $i++) { |
||
2161 | // Append to row's privs list the string representing |
||
2162 | // the privilege |
||
2163 | $char = substr($chars, $i, 1); |
||
2164 | if ($char == '*') { |
||
2165 | $row[4][] = $this->privmap[substr($chars, $i - 1, 1)]; |
||
2166 | View Code Duplication | } elseif ($char == '/') { |
|
2167 | $grantor = substr($chars, $i + 1); |
||
2168 | // Check for quoting |
||
2169 | if (strpos($grantor, '"') === 0) { |
||
2170 | $grantor = substr($grantor, 1, strlen($grantor) - 2); |
||
2171 | $grantor = str_replace('""', '"', $grantor); |
||
2172 | } |
||
2173 | $row[3] = $grantor; |
||
2174 | break; |
||
2175 | } else { |
||
2176 | if (!isset($this->privmap[$char])) { |
||
2177 | return -3; |
||
2178 | } |
||
2179 | |||
2180 | $row[2][] = $this->privmap[$char]; |
||
2181 | } |
||
2182 | } |
||
2183 | |||
2184 | // Append row to temp |
||
2185 | $temp[] = $row; |
||
2186 | } |
||
2187 | |||
2188 | return $temp; |
||
2189 | } |
||
2190 | |||
2191 | public function hasRoles() |
||
2192 | { |
||
2193 | return true; |
||
2194 | } |
||
2195 | |||
2196 | public function hasGrantOption() |
||
2197 | { |
||
2198 | return true; |
||
2199 | } |
||
2200 | |||
2201 | /** |
||
2202 | * Returns extra table definition information that is most usefully |
||
2203 | * dumped after the table contents for speed and efficiency reasons |
||
2204 | * |
||
2205 | * @param $table The table to define |
||
2206 | * @return A string containing the formatted SQL code |
||
2207 | * @return null On error |
||
2208 | */ |
||
2209 | public function getTableDefSuffix($table) |
||
2210 | { |
||
2211 | $sql = ''; |
||
2212 | |||
2213 | // Indexes |
||
2214 | $indexes = $this->getIndexes($table); |
||
2215 | if (!is_object($indexes)) { |
||
2216 | $this->rollbackTransaction(); |
||
2217 | |||
2218 | return null; |
||
2219 | } |
||
2220 | |||
2221 | View Code Duplication | if ($indexes->recordCount() > 0) { |
|
2222 | $sql .= "\n-- Indexes\n\n"; |
||
2223 | while (!$indexes->EOF) { |
||
2224 | $sql .= $indexes->fields['inddef'] . ";\n"; |
||
2225 | |||
2226 | $indexes->moveNext(); |
||
2227 | } |
||
2228 | } |
||
2229 | |||
2230 | // Triggers |
||
2231 | $triggers = $this->getTriggers($table); |
||
2232 | if (!is_object($triggers)) { |
||
2233 | $this->rollbackTransaction(); |
||
2234 | |||
2235 | return null; |
||
2236 | } |
||
2237 | |||
2238 | View Code Duplication | if ($triggers->recordCount() > 0) { |
|
2239 | $sql .= "\n-- Triggers\n\n"; |
||
2240 | while (!$triggers->EOF) { |
||
2241 | $sql .= $triggers->fields['tgdef']; |
||
2242 | $sql .= ";\n"; |
||
2243 | |||
2244 | $triggers->moveNext(); |
||
2245 | } |
||
2246 | } |
||
2247 | |||
2248 | // Rules |
||
2249 | $rules = $this->getRules($table); |
||
2250 | if (!is_object($rules)) { |
||
2251 | $this->rollbackTransaction(); |
||
2252 | |||
2253 | return null; |
||
2254 | } |
||
2255 | |||
2256 | View Code Duplication | if ($rules->recordCount() > 0) { |
|
2257 | $sql .= "\n-- Rules\n\n"; |
||
2258 | while (!$rules->EOF) { |
||
2259 | $sql .= $rules->fields['definition'] . "\n"; |
||
2260 | |||
2261 | $rules->moveNext(); |
||
2262 | } |
||
2263 | } |
||
2264 | |||
2265 | return $sql; |
||
2266 | } |
||
2267 | |||
2268 | /** |
||
2269 | * Grabs a list of indexes for a table |
||
2270 | * |
||
2271 | * @param \PHPPgAdmin\Database\The|string $table The name of a table whose indexes to retrieve |
||
2272 | * @param bool|\PHPPgAdmin\Database\Only $unique Only get unique/pk indexes |
||
2273 | * @return \PHPPgAdmin\Database\A recordset |
||
2274 | */ |
||
2275 | public function getIndexes($table = '', $unique = false) |
||
2276 | { |
||
2277 | $this->clean($table); |
||
2278 | |||
2279 | $sql = " |
||
2280 | SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered, |
||
2281 | pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef |
||
2282 | FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i |
||
2283 | WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid) |
||
2284 | AND c.oid = i.indrelid AND i.indexrelid = c2.oid |
||
2285 | "; |
||
2286 | if ($unique) { |
||
2287 | $sql .= ' AND i.indisunique '; |
||
2288 | } |
||
2289 | |||
2290 | $sql .= ' ORDER BY c2.relname'; |
||
2291 | |||
2292 | return $this->selectSet($sql); |
||
2293 | } |
||
2294 | |||
2295 | /** |
||
2296 | * Grabs a list of triggers on a table |
||
2297 | * |
||
2298 | * @param \PHPPgAdmin\Database\The|string $table The name of a table whose triggers to retrieve |
||
2299 | * @return \PHPPgAdmin\Database\A recordset |
||
2300 | */ |
||
2301 | View Code Duplication | public function getTriggers($table = '') |
|
2302 | { |
||
2303 | $c_schema = $this->_schema; |
||
2304 | $this->clean($c_schema); |
||
2305 | $this->clean($table); |
||
2306 | |||
2307 | $sql = "SELECT |
||
2308 | t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef, |
||
2309 | CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid, |
||
2310 | p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto, |
||
2311 | ns.nspname AS pronamespace |
||
2312 | FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns |
||
2313 | WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
2314 | AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')) |
||
2315 | AND ( tgconstraint = 0 OR NOT EXISTS |
||
2316 | (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c |
||
2317 | ON (d.refclassid = c.tableoid AND d.refobjid = c.oid) |
||
2318 | WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f')) |
||
2319 | AND p.oid=t.tgfoid |
||
2320 | AND p.pronamespace = ns.oid"; |
||
2321 | |||
2322 | return $this->selectSet($sql); |
||
2323 | } |
||
2324 | |||
2325 | /** |
||
2326 | * Returns a list of all rules on a table OR view |
||
2327 | * |
||
2328 | * @param $table The table to find rules for |
||
2329 | * @return A recordset |
||
2330 | */ |
||
2331 | View Code Duplication | public function getRules($table) |
|
2332 | { |
||
2333 | $c_schema = $this->_schema; |
||
2334 | $this->clean($c_schema); |
||
2335 | $this->clean($table); |
||
2336 | |||
2337 | $sql = " |
||
2338 | SELECT * |
||
2339 | FROM pg_catalog.pg_rules |
||
2340 | WHERE |
||
2341 | schemaname='{$c_schema}' AND tablename='{$table}' |
||
2342 | ORDER BY rulename |
||
2343 | "; |
||
2344 | |||
2345 | return $this->selectSet($sql); |
||
2346 | } |
||
2347 | |||
2348 | /** |
||
2349 | * Creates a new table in the database |
||
2350 | * |
||
2351 | * @param $name The name of the table |
||
2352 | * @param $fields The number of fields |
||
2353 | * @param $field An array of field names |
||
2354 | * @param $type An array of field types |
||
2355 | * @param $array An array of '' or '[]' for each type if it's an array or not |
||
2356 | * @param $length An array of field lengths |
||
2357 | * @param $notnull An array of not null |
||
2358 | * @param $default An array of default values |
||
2359 | * @param $withoutoids True if WITHOUT OIDS, false otherwise |
||
2360 | * @param $colcomment An array of comments |
||
2361 | * @param $tblcomment |
||
2362 | * @param $tablespace The tablespace name ('' means none/default) |
||
2363 | * @param $uniquekey An Array indicating the fields that are unique (those indexes that are set) |
||
2364 | * @param $primarykey An Array indicating the field used for the primarykey (those indexes that are set) |
||
2365 | * @return bool|int 0 success |
||
2366 | * @internal param \PHPPgAdmin\Database\Table $comment comment |
||
2367 | */ |
||
2368 | public function createTable( |
||
2369 | $name, |
||
2370 | $fields, |
||
2371 | $field, |
||
2372 | $type, |
||
2373 | $array, |
||
2374 | $length, |
||
2375 | $notnull, |
||
2376 | $default, |
||
2377 | $withoutoids, |
||
2378 | $colcomment, |
||
2379 | $tblcomment, |
||
2380 | $tablespace, |
||
2381 | $uniquekey, |
||
2382 | $primarykey |
||
2383 | ) { |
||
2384 | $f_schema = $this->_schema; |
||
2385 | $this->fieldClean($f_schema); |
||
2386 | $this->fieldClean($name); |
||
2387 | |||
2388 | $status = $this->beginTransaction(); |
||
2389 | if ($status != 0) { |
||
2390 | return -1; |
||
2391 | } |
||
2392 | |||
2393 | $found = false; |
||
2394 | $first = true; |
||
2395 | $comment_sql = ''; //Accumulate comments for the columns |
||
2396 | $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" ("; |
||
2397 | for ($i = 0; $i < $fields; $i++) { |
||
2398 | $this->fieldClean($field[$i]); |
||
2399 | $this->clean($type[$i]); |
||
2400 | $this->clean($length[$i]); |
||
2401 | $this->clean($colcomment[$i]); |
||
2402 | |||
2403 | // Skip blank columns - for user convenience |
||
2404 | if ($field[$i] == '' || $type[$i] == '') { |
||
2405 | continue; |
||
2406 | } |
||
2407 | |||
2408 | // If not the first column, add a comma |
||
2409 | if (!$first) { |
||
2410 | $sql .= ', '; |
||
2411 | } else { |
||
2412 | $first = false; |
||
2413 | } |
||
2414 | |||
2415 | View Code Duplication | switch ($type[$i]) { |
|
2416 | // Have to account for weird placing of length for with/without |
||
2417 | // time zone types |
||
2418 | case 'timestamp with time zone': |
||
2419 | case 'timestamp without time zone': |
||
2420 | $qual = substr($type[$i], 9); |
||
2421 | $sql .= "\"{$field[$i]}\" timestamp"; |
||
2422 | if ($length[$i] != '') { |
||
2423 | $sql .= "({$length[$i]})"; |
||
2424 | } |
||
2425 | |||
2426 | $sql .= $qual; |
||
2427 | break; |
||
2428 | case 'time with time zone': |
||
2429 | case 'time without time zone': |
||
2430 | $qual = substr($type[$i], 4); |
||
2431 | $sql .= "\"{$field[$i]}\" time"; |
||
2432 | if ($length[$i] != '') { |
||
2433 | $sql .= "({$length[$i]})"; |
||
2434 | } |
||
2435 | |||
2436 | $sql .= $qual; |
||
2437 | break; |
||
2438 | default: |
||
2439 | $sql .= "\"{$field[$i]}\" {$type[$i]}"; |
||
2440 | if ($length[$i] != '') { |
||
2441 | $sql .= "({$length[$i]})"; |
||
2442 | } |
||
2443 | } |
||
2444 | // Add array qualifier if necessary |
||
2445 | if ($array[$i] == '[]') { |
||
2446 | $sql .= '[]'; |
||
2447 | } |
||
2448 | |||
2449 | // Add other qualifiers |
||
2450 | if (!isset($primarykey[$i])) { |
||
2451 | if (isset($uniquekey[$i])) { |
||
2452 | $sql .= ' UNIQUE'; |
||
2453 | } |
||
2454 | |||
2455 | if (isset($notnull[$i])) { |
||
2456 | $sql .= ' NOT NULL'; |
||
2457 | } |
||
2458 | } |
||
2459 | if ($default[$i] != '') { |
||
2460 | $sql .= " DEFAULT {$default[$i]}"; |
||
2461 | } |
||
2462 | |||
2463 | View Code Duplication | if ($colcomment[$i] != '') { |
|
2464 | $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n"; |
||
2465 | } |
||
2466 | |||
2467 | $found = true; |
||
2468 | } |
||
2469 | |||
2470 | if (!$found) { |
||
2471 | return -1; |
||
2472 | } |
||
2473 | |||
2474 | // PRIMARY KEY |
||
2475 | $primarykeycolumns = []; |
||
2476 | for ($i = 0; $i < $fields; $i++) { |
||
2477 | if (isset($primarykey[$i])) { |
||
2478 | $primarykeycolumns[] = "\"{$field[$i]}\""; |
||
2479 | } |
||
2480 | } |
||
2481 | if (count($primarykeycolumns) > 0) { |
||
2482 | $sql .= ', PRIMARY KEY (' . implode(', ', $primarykeycolumns) . ')'; |
||
2483 | } |
||
2484 | |||
2485 | $sql .= ')'; |
||
2486 | |||
2487 | // WITHOUT OIDS |
||
2488 | if ($withoutoids) { |
||
2489 | $sql .= ' WITHOUT OIDS'; |
||
2490 | } else { |
||
2491 | $sql .= ' WITH OIDS'; |
||
2492 | } |
||
2493 | |||
2494 | // Tablespace |
||
2495 | if ($this->hasTablespaces() && $tablespace != '') { |
||
2496 | $this->fieldClean($tablespace); |
||
2497 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||
2498 | } |
||
2499 | |||
2500 | $status = $this->execute($sql); |
||
2501 | if ($status) { |
||
2502 | $this->rollbackTransaction(); |
||
2503 | |||
2504 | return -1; |
||
2505 | } |
||
2506 | |||
2507 | View Code Duplication | if ($tblcomment != '') { |
|
2508 | $status = $this->setComment('TABLE', '', $name, $tblcomment, true); |
||
2509 | if ($status) { |
||
2510 | $this->rollbackTransaction(); |
||
2511 | |||
2512 | return -1; |
||
2513 | } |
||
2514 | } |
||
2515 | |||
2516 | View Code Duplication | if ($comment_sql != '') { |
|
2517 | $status = $this->execute($comment_sql); |
||
2518 | if ($status) { |
||
2519 | $this->rollbackTransaction(); |
||
2520 | |||
2521 | return -1; |
||
2522 | } |
||
2523 | } |
||
2524 | |||
2525 | return $this->endTransaction(); |
||
2526 | } |
||
2527 | |||
2528 | /** |
||
2529 | * Creates a new table in the database copying attribs and other properties from another table |
||
2530 | * |
||
2531 | * @param $name The name of the table |
||
2532 | * @param $like an array giving the schema ans the name of the table from which attribs are copying |
||
2533 | * from: array( |
||
2534 | * 'table' => table name, |
||
2535 | * 'schema' => the schema name, |
||
2536 | * ) |
||
2537 | * @param bool|\PHPPgAdmin\Database\if $defaults if true, copy the defaults values as well |
||
2538 | * @param bool|\PHPPgAdmin\Database\if $constraints if true, copy the constraints as well (CHECK on table & attr) |
||
2539 | * @param bool $idx |
||
2540 | * @param \PHPPgAdmin\Database\The|string $tablespace The tablespace name ('' means none/default) |
||
2541 | * @return bool|int |
||
2542 | */ |
||
2543 | public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '') |
||
2544 | { |
||
2545 | $f_schema = $this->_schema; |
||
2546 | $this->fieldClean($f_schema); |
||
2547 | $this->fieldClean($name); |
||
2548 | $this->fieldClean($like['schema']); |
||
2549 | $this->fieldClean($like['table']); |
||
2550 | $like = "\"{$like['schema']}\".\"{$like['table']}\""; |
||
2551 | |||
2552 | $status = $this->beginTransaction(); |
||
2553 | if ($status != 0) { |
||
2554 | return -1; |
||
2555 | } |
||
2556 | |||
2557 | $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}"; |
||
2558 | |||
2559 | if ($defaults) { |
||
2560 | $sql .= ' INCLUDING DEFAULTS'; |
||
2561 | } |
||
2562 | |||
2563 | if ($this->hasCreateTableLikeWithConstraints() && $constraints) { |
||
2564 | $sql .= ' INCLUDING CONSTRAINTS'; |
||
2565 | } |
||
2566 | |||
2567 | if ($this->hasCreateTableLikeWithIndexes() && $idx) { |
||
2568 | $sql .= ' INCLUDING INDEXES'; |
||
2569 | } |
||
2570 | |||
2571 | $sql .= ')'; |
||
2572 | |||
2573 | if ($this->hasTablespaces() && $tablespace != '') { |
||
2574 | $this->fieldClean($tablespace); |
||
2575 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||
2576 | } |
||
2577 | |||
2578 | $status = $this->execute($sql); |
||
2579 | if ($status) { |
||
2580 | $this->rollbackTransaction(); |
||
2581 | |||
2582 | return -1; |
||
2583 | } |
||
2584 | |||
2585 | return $this->endTransaction(); |
||
2586 | } |
||
2587 | |||
2588 | public function hasCreateTableLikeWithConstraints() |
||
2589 | { |
||
2590 | return true; |
||
2591 | } |
||
2592 | |||
2593 | public function hasCreateTableLikeWithIndexes() |
||
2594 | { |
||
2595 | return true; |
||
2596 | } |
||
2597 | |||
2598 | /** |
||
2599 | * Alter table properties |
||
2600 | * |
||
2601 | * @param $table The name of the table |
||
2602 | * @param $name The new name for the table |
||
2603 | * @param $owner The new owner for the table |
||
2604 | * @param $schema The new schema for the table |
||
2605 | * @param $comment The comment on the table |
||
2606 | * @param $tablespace The new tablespace for the table ('' means leave as is) |
||
2607 | * @return bool|int 0 success |
||
2608 | */ |
||
2609 | View Code Duplication | public function alterTable($table, $name, $owner, $schema, $comment, $tablespace) |
|
2610 | { |
||
2611 | $data = $this->getTable($table); |
||
2612 | |||
2613 | if ($data->recordCount() != 1) { |
||
2614 | return -2; |
||
2615 | } |
||
2616 | |||
2617 | $status = $this->beginTransaction(); |
||
2618 | if ($status != 0) { |
||
2619 | $this->rollbackTransaction(); |
||
2620 | |||
2621 | return -1; |
||
2622 | } |
||
2623 | |||
2624 | $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace); |
||
2625 | |||
2626 | if ($status != 0) { |
||
2627 | $this->rollbackTransaction(); |
||
2628 | |||
2629 | return $status; |
||
2630 | } |
||
2631 | |||
2632 | return $this->endTransaction(); |
||
2633 | } |
||
2634 | |||
2635 | /** |
||
2636 | * Protected method which alter a table |
||
2637 | * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION |
||
2638 | * |
||
2639 | * @param $tblrs The table recordSet returned by getTable() |
||
2640 | * @param $name The new name for the table |
||
2641 | * @param $owner The new owner for the table |
||
2642 | * @param $schema The new schema for the table |
||
2643 | * @param $comment The comment on the table |
||
2644 | * @param $tablespace The new tablespace for the table ('' means leave as is) |
||
2645 | * @return int 0 success |
||
2646 | */ |
||
2647 | protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) |
||
2648 | { |
||
2649 | $this->fieldArrayClean($tblrs->fields); |
||
2650 | |||
2651 | // Comment |
||
2652 | $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment); |
||
2653 | if ($status != 0) { |
||
2654 | return -4; |
||
2655 | } |
||
2656 | |||
2657 | // Owner |
||
2658 | $this->fieldClean($owner); |
||
2659 | $status = $this->alterTableOwner($tblrs, $owner); |
||
2660 | if ($status != 0) { |
||
2661 | return -5; |
||
2662 | } |
||
2663 | |||
2664 | // Tablespace |
||
2665 | $this->fieldClean($tablespace); |
||
2666 | $status = $this->alterTableTablespace($tblrs, $tablespace); |
||
2667 | if ($status != 0) { |
||
2668 | return -6; |
||
2669 | } |
||
2670 | |||
2671 | // Rename |
||
2672 | $this->fieldClean($name); |
||
2673 | $status = $this->alterTableName($tblrs, $name); |
||
2674 | if ($status != 0) { |
||
2675 | return -3; |
||
2676 | } |
||
2677 | |||
2678 | // Schema |
||
2679 | $this->fieldClean($schema); |
||
2680 | $status = $this->alterTableSchema($tblrs, $schema); |
||
2681 | if ($status != 0) { |
||
2682 | return -7; |
||
2683 | } |
||
2684 | |||
2685 | return 0; |
||
2686 | } |
||
2687 | |||
2688 | /** |
||
2689 | * Alter a table's owner |
||
2690 | * /!\ this function is called from _alterTable which take care of escaping fields |
||
2691 | * |
||
2692 | * @param $tblrs The table RecordSet returned by getTable() |
||
2693 | * @param null $owner |
||
2694 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
2695 | * @internal param \PHPPgAdmin\Database\The $name new table's owner |
||
2696 | */ |
||
2697 | View Code Duplication | public function alterTableOwner($tblrs, $owner = null) |
|
2698 | { |
||
2699 | /* vars cleaned in _alterTable */ |
||
2700 | if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) { |
||
2701 | $f_schema = $this->_schema; |
||
2702 | $this->fieldClean($f_schema); |
||
2703 | // If owner has been changed, then do the alteration. We are |
||
2704 | // careful to avoid this generally as changing owner is a |
||
2705 | // superuser only function. |
||
2706 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\""; |
||
2707 | |||
2708 | return $this->execute($sql); |
||
2709 | } |
||
2710 | |||
2711 | return 0; |
||
2712 | } |
||
2713 | |||
2714 | /** |
||
2715 | * Alter a table's tablespace |
||
2716 | * /!\ this function is called from _alterTable which take care of escaping fields |
||
2717 | * |
||
2718 | * @param $tblrs The table RecordSet returned by getTable() |
||
2719 | * @param null $tablespace |
||
2720 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
2721 | * @internal param \PHPPgAdmin\Database\The $name new table's tablespace |
||
2722 | */ |
||
2723 | View Code Duplication | public function alterTableTablespace($tblrs, $tablespace = null) |
|
2724 | { |
||
2725 | /* vars cleaned in _alterTable */ |
||
2726 | if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) { |
||
2727 | $f_schema = $this->_schema; |
||
2728 | $this->fieldClean($f_schema); |
||
2729 | |||
2730 | // If tablespace has been changed, then do the alteration. We |
||
2731 | // don't want to do this unnecessarily. |
||
2732 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\""; |
||
2733 | |||
2734 | return $this->execute($sql); |
||
2735 | } |
||
2736 | |||
2737 | return 0; |
||
2738 | } |
||
2739 | |||
2740 | /** |
||
2741 | * Alter a table's name |
||
2742 | * /!\ this function is called from _alterTable which take care of escaping fields |
||
2743 | * |
||
2744 | * @param $tblrs The table RecordSet returned by getTable() |
||
2745 | * @param $name The new table's name |
||
2746 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
2747 | */ |
||
2748 | View Code Duplication | public function alterTableName($tblrs, $name = null) |
|
2749 | { |
||
2750 | /* vars cleaned in _alterTable */ |
||
2751 | // Rename (only if name has changed) |
||
2752 | if (!empty($name) && ($name != $tblrs->fields['relname'])) { |
||
2753 | $f_schema = $this->_schema; |
||
2754 | $this->fieldClean($f_schema); |
||
2755 | |||
2756 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\""; |
||
2757 | $status = $this->execute($sql); |
||
2758 | if ($status == 0) { |
||
2759 | $tblrs->fields['relname'] = $name; |
||
2760 | } else { |
||
2761 | return $status; |
||
2762 | } |
||
2763 | } |
||
2764 | |||
2765 | return 0; |
||
2766 | } |
||
2767 | |||
2768 | // Row functions |
||
2769 | |||
2770 | /** |
||
2771 | * Alter a table's schema |
||
2772 | * /!\ this function is called from _alterTable which take care of escaping fields |
||
2773 | * |
||
2774 | * @param $tblrs The table RecordSet returned by getTable() |
||
2775 | * @param null $schema |
||
2776 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
2777 | * @internal param \PHPPgAdmin\Database\The $name new table's schema |
||
2778 | */ |
||
2779 | View Code Duplication | public function alterTableSchema($tblrs, $schema = null) |
|
2780 | { |
||
2781 | /* vars cleaned in _alterTable */ |
||
2782 | if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) { |
||
2783 | $f_schema = $this->_schema; |
||
2784 | $this->fieldClean($f_schema); |
||
2785 | // If tablespace has been changed, then do the alteration. We |
||
2786 | // don't want to do this unnecessarily. |
||
2787 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\""; |
||
2788 | |||
2789 | return $this->execute($sql); |
||
2790 | } |
||
2791 | |||
2792 | return 0; |
||
2793 | } |
||
2794 | |||
2795 | /** |
||
2796 | * Empties a table in the database |
||
2797 | * |
||
2798 | * @param $table The table to be emptied |
||
2799 | * @return \PHPPgAdmin\Database\A 0 success |
||
2800 | */ |
||
2801 | public function emptyTable($table) |
||
2802 | { |
||
2803 | $f_schema = $this->_schema; |
||
2804 | $this->fieldClean($f_schema); |
||
2805 | $this->fieldClean($table); |
||
2806 | |||
2807 | $sql = "DELETE FROM \"{$f_schema}\".\"{$table}\""; |
||
2808 | |||
2809 | return $this->execute($sql); |
||
2810 | } |
||
2811 | |||
2812 | /** |
||
2813 | * Removes a table from the database |
||
2814 | * |
||
2815 | * @param $table The table to drop |
||
2816 | * @param $cascade True to cascade drop, false to restrict |
||
2817 | * @return \PHPPgAdmin\Database\A 0 success |
||
2818 | */ |
||
2819 | View Code Duplication | public function dropTable($table, $cascade) |
|
2820 | { |
||
2821 | $f_schema = $this->_schema; |
||
2822 | $this->fieldClean($f_schema); |
||
2823 | $this->fieldClean($table); |
||
2824 | |||
2825 | $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\""; |
||
2826 | if ($cascade) { |
||
2827 | $sql .= ' CASCADE'; |
||
2828 | } |
||
2829 | |||
2830 | return $this->execute($sql); |
||
2831 | } |
||
2832 | |||
2833 | /** |
||
2834 | * Add a new column to a table |
||
2835 | * |
||
2836 | * @param $table The table to add to |
||
2837 | * @param $column The name of the new column |
||
2838 | * @param $type The type of the column |
||
2839 | * @param $array True if array type, false otherwise |
||
2840 | * @param $length The optional size of the column (ie. 30 for varchar(30)) |
||
2841 | * @param $notnull True if NOT NULL, false otherwise |
||
2842 | * @param $default The default for the column. '' for none. |
||
2843 | * @param $comment |
||
2844 | * @return bool|int 0 success |
||
2845 | */ |
||
2846 | public function addColumn($table, $column, $type, $array, $length, $notnull, $default, $comment) |
||
2847 | { |
||
2848 | $f_schema = $this->_schema; |
||
2849 | $this->fieldClean($f_schema); |
||
2850 | $this->fieldClean($table); |
||
2851 | $this->fieldClean($column); |
||
2852 | $this->clean($type); |
||
2853 | $this->clean($length); |
||
2854 | |||
2855 | if ($length == '') { |
||
2856 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}"; |
||
2857 | } else { |
||
2858 | switch ($type) { |
||
2859 | // Have to account for weird placing of length for with/without |
||
2860 | // time zone types |
||
2861 | case 'timestamp with time zone': |
||
2862 | View Code Duplication | case 'timestamp without time zone': |
|
2863 | $qual = substr($type, 9); |
||
2864 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" timestamp({$length}){$qual}"; |
||
2865 | break; |
||
2866 | case 'time with time zone': |
||
2867 | View Code Duplication | case 'time without time zone': |
|
2868 | $qual = substr($type, 4); |
||
2869 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}"; |
||
2870 | break; |
||
2871 | default: |
||
2872 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}({$length})"; |
||
2873 | } |
||
2874 | } |
||
2875 | |||
2876 | // Add array qualifier, if requested |
||
2877 | if ($array) { |
||
2878 | $sql .= '[]'; |
||
2879 | } |
||
2880 | |||
2881 | // If we have advanced column adding, add the extra qualifiers |
||
2882 | if ($this->hasCreateFieldWithConstraints()) { |
||
2883 | // NOT NULL clause |
||
2884 | if ($notnull) { |
||
2885 | $sql .= ' NOT NULL'; |
||
2886 | } |
||
2887 | |||
2888 | // DEFAULT clause |
||
2889 | if ($default != '') { |
||
2890 | $sql .= ' DEFAULT ' . $default; |
||
2891 | } |
||
2892 | } |
||
2893 | |||
2894 | $status = $this->beginTransaction(); |
||
2895 | if ($status != 0) { |
||
2896 | return -1; |
||
2897 | } |
||
2898 | |||
2899 | $status = $this->execute($sql); |
||
2900 | if ($status != 0) { |
||
2901 | $this->rollbackTransaction(); |
||
2902 | |||
2903 | return -1; |
||
2904 | } |
||
2905 | |||
2906 | $status = $this->setComment('COLUMN', $column, $table, $comment); |
||
2907 | if ($status != 0) { |
||
2908 | $this->rollbackTransaction(); |
||
2909 | |||
2910 | return -1; |
||
2911 | } |
||
2912 | |||
2913 | return $this->endTransaction(); |
||
2914 | } |
||
2915 | |||
2916 | // Sequence functions |
||
2917 | |||
2918 | public function hasCreateFieldWithConstraints() |
||
2919 | { |
||
2920 | return true; |
||
2921 | } |
||
2922 | |||
2923 | /** |
||
2924 | * Alters a column in a table |
||
2925 | * |
||
2926 | * @param $table The table in which the column resides |
||
2927 | * @param $column The column to alter |
||
2928 | * @param $name The new name for the column |
||
2929 | * @param $notnull (boolean) True if not null, false otherwise |
||
2930 | * @param $oldnotnull (boolean) True if column is already not null, false otherwise |
||
2931 | * @param $default The new default for the column |
||
2932 | * @param $olddefault The old default for the column |
||
2933 | * @param $type The new type for the column |
||
2934 | * @param $length The optional size of the column (ie. 30 for varchar(30)) |
||
2935 | * @param $array True if array type, false otherwise |
||
2936 | * @param $oldtype The old type for the column |
||
2937 | * @param $comment Comment for the column |
||
2938 | * @return array 0 success |
||
2939 | */ |
||
2940 | public function alterColumn( |
||
2941 | $table, |
||
2942 | $column, |
||
2943 | $name, |
||
2944 | $notnull, |
||
2945 | $oldnotnull, |
||
2946 | $default, |
||
2947 | $olddefault, |
||
2948 | $type, |
||
2949 | $length, |
||
2950 | $array, |
||
2951 | $oldtype, |
||
2952 | $comment |
||
2953 | ) { |
||
2954 | // Begin transaction |
||
2955 | $status = $this->beginTransaction(); |
||
2956 | $sql = ''; |
||
2957 | if ($status != 0) { |
||
2958 | $this->rollbackTransaction(); |
||
2959 | |||
2960 | return [-6, $sql]; |
||
2961 | } |
||
2962 | |||
2963 | // Rename the column, if it has been changed |
||
2964 | View Code Duplication | if ($column != $name) { |
|
2965 | $status = $this->renameColumn($table, $column, $name); |
||
2966 | if ($status != 0) { |
||
2967 | $this->rollbackTransaction(); |
||
2968 | |||
2969 | return [-4, $sql]; |
||
2970 | } |
||
2971 | } |
||
2972 | |||
2973 | $f_schema = $this->_schema; |
||
2974 | $this->fieldClean($f_schema); |
||
2975 | $this->fieldClean($name); |
||
2976 | $this->fieldClean($table); |
||
2977 | $this->fieldClean($column); |
||
2978 | |||
2979 | $toAlter = []; |
||
2980 | // Create the command for changing nullability |
||
2981 | if ($notnull != $oldnotnull) { |
||
2982 | $toAlter[] = "ALTER COLUMN \"{$name}\" " . ($notnull ? 'SET' : 'DROP') . ' NOT NULL'; |
||
2983 | } |
||
2984 | |||
2985 | // Add default, if it has changed |
||
2986 | if ($default != $olddefault) { |
||
2987 | if ($default == '') { |
||
2988 | $toAlter[] = "ALTER COLUMN \"{$name}\" DROP DEFAULT"; |
||
2989 | } else { |
||
2990 | $toAlter[] = "ALTER COLUMN \"{$name}\" SET DEFAULT {$default}"; |
||
2991 | } |
||
2992 | } |
||
2993 | |||
2994 | // Add type, if it has changed |
||
2995 | if ($length == '') { |
||
2996 | $ftype = $type; |
||
2997 | } else { |
||
2998 | switch ($type) { |
||
2999 | // Have to account for weird placing of length for with/without |
||
3000 | // time zone types |
||
3001 | case 'timestamp with time zone': |
||
3002 | case 'timestamp without time zone': |
||
3003 | $qual = substr($type, 9); |
||
3004 | $ftype = "timestamp({$length}){$qual}"; |
||
3005 | break; |
||
3006 | case 'time with time zone': |
||
3007 | case 'time without time zone': |
||
3008 | $qual = substr($type, 4); |
||
3009 | $ftype = "time({$length}){$qual}"; |
||
3010 | break; |
||
3011 | default: |
||
3012 | $ftype = "{$type}({$length})"; |
||
3013 | } |
||
3014 | } |
||
3015 | |||
3016 | // Add array qualifier, if requested |
||
3017 | if ($array) { |
||
3018 | $ftype .= '[]'; |
||
3019 | } |
||
3020 | |||
3021 | if ($ftype != $oldtype) { |
||
3022 | $toAlter[] = "ALTER COLUMN \"{$name}\" TYPE {$ftype}"; |
||
3023 | } |
||
3024 | |||
3025 | // Attempt to process the batch alteration, if anything has been changed |
||
3026 | if (!empty($toAlter)) { |
||
3027 | // Initialise an empty SQL string |
||
3028 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" " |
||
3029 | . implode(',', $toAlter); |
||
3030 | |||
3031 | $status = $this->execute($sql); |
||
3032 | if ($status != 0) { |
||
3033 | $this->rollbackTransaction(); |
||
3034 | |||
3035 | return [-1, $sql]; |
||
3036 | } |
||
3037 | } |
||
3038 | |||
3039 | // Update the comment on the column |
||
3040 | $status = $this->setComment('COLUMN', $name, $table, $comment); |
||
3041 | if ($status != 0) { |
||
3042 | $this->rollbackTransaction(); |
||
3043 | |||
3044 | return [-5, $sql]; |
||
3045 | } |
||
3046 | |||
3047 | return [$this->endTransaction(), $sql]; |
||
3048 | } |
||
3049 | |||
3050 | /** |
||
3051 | * Renames a column in a table |
||
3052 | * |
||
3053 | * @param $table The table containing the column to be renamed |
||
3054 | * @param $column The column to be renamed |
||
3055 | * @param $newName The new name for the column |
||
3056 | * @return \PHPPgAdmin\Database\A 0 success |
||
3057 | */ |
||
3058 | public function renameColumn($table, $column, $newName) |
||
3059 | { |
||
3060 | $f_schema = $this->_schema; |
||
3061 | $this->fieldClean($f_schema); |
||
3062 | $this->fieldClean($table); |
||
3063 | $this->fieldClean($column); |
||
3064 | $this->fieldClean($newName); |
||
3065 | |||
3066 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\""; |
||
3067 | |||
3068 | return $this->execute($sql); |
||
3069 | } |
||
3070 | |||
3071 | /** |
||
3072 | * Sets default value of a column |
||
3073 | * |
||
3074 | * @param $table The table from which to drop |
||
3075 | * @param $column The column name to set |
||
3076 | * @param $default The new default value |
||
3077 | * @return \PHPPgAdmin\Database\A 0 success |
||
3078 | */ |
||
3079 | View Code Duplication | public function setColumnDefault($table, $column, $default) |
|
3080 | { |
||
3081 | $f_schema = $this->_schema; |
||
3082 | $this->fieldClean($f_schema); |
||
3083 | $this->fieldClean($table); |
||
3084 | $this->fieldClean($column); |
||
3085 | |||
3086 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}"; |
||
3087 | |||
3088 | return $this->execute($sql); |
||
3089 | } |
||
3090 | |||
3091 | /** |
||
3092 | * Sets whether or not a column can contain NULLs |
||
3093 | * |
||
3094 | * @param $table The table that contains the column |
||
3095 | * @param $column The column to alter |
||
3096 | * @param $state True to set null, false to set not null |
||
3097 | * @return \PHPPgAdmin\Database\A 0 success |
||
3098 | */ |
||
3099 | public function setColumnNull($table, $column, $state) |
||
3100 | { |
||
3101 | $f_schema = $this->_schema; |
||
3102 | $this->fieldClean($f_schema); |
||
3103 | $this->fieldClean($table); |
||
3104 | $this->fieldClean($column); |
||
3105 | |||
3106 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . ($state ? 'DROP' : 'SET') . ' NOT NULL'; |
||
3107 | |||
3108 | return $this->execute($sql); |
||
3109 | } |
||
3110 | |||
3111 | /** |
||
3112 | * Drops a column from a table |
||
3113 | * |
||
3114 | * @param $table The table from which to drop a column |
||
3115 | * @param $column The column to be dropped |
||
3116 | * @param $cascade True to cascade drop, false to restrict |
||
3117 | * @return \PHPPgAdmin\Database\A 0 success |
||
3118 | */ |
||
3119 | View Code Duplication | public function dropColumn($table, $column, $cascade) |
|
3120 | { |
||
3121 | $f_schema = $this->_schema; |
||
3122 | $this->fieldClean($f_schema); |
||
3123 | $this->fieldClean($table); |
||
3124 | $this->fieldClean($column); |
||
3125 | |||
3126 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DROP COLUMN \"{$column}\""; |
||
3127 | if ($cascade) { |
||
3128 | $sql .= ' CASCADE'; |
||
3129 | } |
||
3130 | |||
3131 | return $this->execute($sql); |
||
3132 | } |
||
3133 | |||
3134 | /** |
||
3135 | * Drops default value of a column |
||
3136 | * |
||
3137 | * @param $table The table from which to drop |
||
3138 | * @param $column The column name to drop default |
||
3139 | * @return \PHPPgAdmin\Database\A 0 success |
||
3140 | */ |
||
3141 | View Code Duplication | public function dropColumnDefault($table, $column) |
|
3142 | { |
||
3143 | $f_schema = $this->_schema; |
||
3144 | $this->fieldClean($f_schema); |
||
3145 | $this->fieldClean($table); |
||
3146 | $this->fieldClean($column); |
||
3147 | |||
3148 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT"; |
||
3149 | |||
3150 | return $this->execute($sql); |
||
3151 | } |
||
3152 | |||
3153 | /** |
||
3154 | * Sets up the data object for a dump. eg. Starts the appropriate |
||
3155 | * transaction, sets variables, etc. |
||
3156 | * |
||
3157 | * @return int 0 success |
||
3158 | */ |
||
3159 | public function beginDump() |
||
3160 | { |
||
3161 | // Begin serializable transaction (to dump consistent data) |
||
3162 | $status = $this->beginTransaction(); |
||
3163 | if ($status != 0) { |
||
3164 | return -1; |
||
3165 | } |
||
3166 | |||
3167 | // Set serializable |
||
3168 | $sql = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE'; |
||
3169 | $status = $this->execute($sql); |
||
3170 | if ($status != 0) { |
||
3171 | $this->rollbackTransaction(); |
||
3172 | |||
3173 | return -1; |
||
3174 | } |
||
3175 | |||
3176 | // Set datestyle to ISO |
||
3177 | $sql = 'SET DATESTYLE = ISO'; |
||
3178 | $status = $this->execute($sql); |
||
3179 | if ($status != 0) { |
||
3180 | $this->rollbackTransaction(); |
||
3181 | |||
3182 | return -1; |
||
3183 | } |
||
3184 | |||
3185 | // Set extra_float_digits to 2 |
||
3186 | $sql = 'SET extra_float_digits TO 2'; |
||
3187 | $status = $this->execute($sql); |
||
3188 | if ($status != 0) { |
||
3189 | $this->rollbackTransaction(); |
||
3190 | |||
3191 | return -1; |
||
3192 | } |
||
3193 | |||
3194 | return 0; |
||
3195 | } |
||
3196 | |||
3197 | /** |
||
3198 | * Ends the data object for a dump. |
||
3199 | * |
||
3200 | * @return bool 0 success |
||
3201 | */ |
||
3202 | public function endDump() |
||
3203 | { |
||
3204 | return $this->endTransaction(); |
||
3205 | } |
||
3206 | |||
3207 | /** |
||
3208 | * Returns a recordset of all columns in a relation. Used for data export. |
||
3209 | * @@ Note: Really needs to use a cursor |
||
3210 | * |
||
3211 | * @param $relation The name of a relation |
||
3212 | * @param $oids |
||
3213 | * @return \PHPPgAdmin\Database\A recordset on success |
||
3214 | */ |
||
3215 | public function dumpRelation($relation, $oids) |
||
3216 | { |
||
3217 | $this->fieldClean($relation); |
||
3218 | |||
3219 | // Actually retrieve the rows |
||
3220 | if ($oids) { |
||
3221 | $oid_str = $this->id . ', '; |
||
3222 | } else { |
||
3223 | $oid_str = ''; |
||
3224 | } |
||
3225 | |||
3226 | return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\""); |
||
3227 | } |
||
3228 | |||
3229 | /** |
||
3230 | * Returns all available autovacuum per table information. |
||
3231 | * |
||
3232 | * @param \PHPPgAdmin\Database\if|string $table if given, return autovacuum info for the given table or return all informations for all table |
||
3233 | * @return \PHPPgAdmin\Database\A recordset |
||
3234 | */ |
||
3235 | public function getTableAutovacuum($table = '') |
||
3236 | { |
||
3237 | $sql = ''; |
||
3238 | |||
3239 | if ($table !== '') { |
||
3240 | $this->clean($table); |
||
3241 | $c_schema = $this->_schema; |
||
3242 | $this->clean($c_schema); |
||
3243 | |||
3244 | $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions |
||
3245 | FROM pg_class c |
||
3246 | LEFT JOIN pg_namespace n ON n.oid = c.relnamespace |
||
3247 | WHERE c.relkind = 'r'::\"char\" |
||
3248 | AND n.nspname NOT IN ('pg_catalog','information_schema') |
||
3249 | AND c.reloptions IS NOT NULL |
||
3250 | AND c.relname = '{$table}' AND n.nspname = '{$c_schema}' |
||
3251 | ORDER BY nspname, relname"; |
||
3252 | } else { |
||
3253 | $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions |
||
3254 | FROM pg_class c |
||
3255 | LEFT JOIN pg_namespace n ON n.oid = c.relnamespace |
||
3256 | WHERE c.relkind = 'r'::\"char\" |
||
3257 | AND n.nspname NOT IN ('pg_catalog','information_schema') |
||
3258 | AND c.reloptions IS NOT NULL |
||
3259 | ORDER BY nspname, relname"; |
||
3260 | } |
||
3261 | |||
3262 | /* tmp var to parse the results */ |
||
3263 | $_autovacs = $this->selectSet($sql); |
||
3264 | |||
3265 | /* result aray to return as RS */ |
||
3266 | $autovacs = []; |
||
3267 | while (!$_autovacs->EOF) { |
||
3268 | $_ = [ |
||
3269 | 'nspname' => $_autovacs->fields['nspname'], |
||
3270 | 'relname' => $_autovacs->fields['relname'], |
||
3271 | ]; |
||
3272 | |||
3273 | foreach (explode(',', $_autovacs->fields['reloptions']) as $var) { |
||
3274 | list($o, $v) = explode('=', $var); |
||
3275 | $_[$o] = $v; |
||
3276 | } |
||
3277 | |||
3278 | $autovacs[] = $_; |
||
3279 | |||
3280 | $_autovacs->moveNext(); |
||
3281 | } |
||
3282 | |||
3283 | return new \PHPPgAdmin\ArrayRecordSet($autovacs); |
||
3284 | } |
||
3285 | |||
3286 | /** |
||
3287 | * Get the fields for uniquely identifying a row in a table |
||
3288 | * |
||
3289 | * @param $table The table for which to retrieve the identifier |
||
3290 | * @return An array mapping attribute number to attribute name, empty for no identifiers |
||
3291 | * @return -1 error |
||
3292 | */ |
||
3293 | public function getRowIdentifier($table) |
||
3294 | { |
||
3295 | $oldtable = $table; |
||
3296 | $c_schema = $this->_schema; |
||
3297 | $this->clean($c_schema); |
||
3298 | $this->clean($table); |
||
3299 | |||
3300 | $status = $this->beginTransaction(); |
||
3301 | if ($status != 0) { |
||
3302 | return -1; |
||
3303 | } |
||
3304 | |||
3305 | // Get the first primary or unique index (sorting primary keys first) that |
||
3306 | // is NOT a partial index. |
||
3307 | $sql = " |
||
3308 | SELECT indrelid, indkey |
||
3309 | FROM pg_catalog.pg_index |
||
3310 | WHERE indisunique AND indrelid=( |
||
3311 | SELECT oid FROM pg_catalog.pg_class |
||
3312 | WHERE relname='{$table}' AND relnamespace=( |
||
3313 | SELECT oid FROM pg_catalog.pg_namespace |
||
3314 | WHERE nspname='{$c_schema}' |
||
3315 | ) |
||
3316 | ) AND indpred IS NULL AND indexprs IS NULL |
||
3317 | ORDER BY indisprimary DESC LIMIT 1"; |
||
3318 | $rs = $this->selectSet($sql); |
||
3319 | |||
3320 | // If none, check for an OID column. Even though OIDs can be duplicated, the edit and delete row |
||
3321 | // functions check that they're only modiying a single row. Otherwise, return empty array. |
||
3322 | if ($rs->recordCount() == 0) { |
||
3323 | // Check for OID column |
||
3324 | $temp = []; |
||
3325 | if ($this->hasObjectID($table)) { |
||
3326 | $temp = ['oid']; |
||
3327 | } |
||
3328 | $this->endTransaction(); |
||
3329 | |||
3330 | return $temp; |
||
3331 | } // Otherwise find the names of the keys |
||
3332 | |||
3333 | $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey'])); |
||
3334 | if (!is_array($attnames)) { |
||
3335 | $this->rollbackTransaction(); |
||
3336 | |||
3337 | return -1; |
||
3338 | } |
||
3339 | |||
3340 | $this->endTransaction(); |
||
3341 | |||
3342 | return $attnames; |
||
3343 | } |
||
3344 | |||
3345 | /** |
||
3346 | * Adds a new row to a table |
||
3347 | * |
||
3348 | * @param $table The table in which to insert |
||
3349 | * @param $fields Array of given field in values |
||
3350 | * @param $values Array of new values for the row |
||
3351 | * @param $nulls An array mapping column => something if it is to be null |
||
3352 | * @param $format An array of the data type (VALUE or EXPRESSION) |
||
3353 | * @param $types An array of field types |
||
3354 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
3355 | */ |
||
3356 | public function insertRow($table, $fields, $values, $nulls, $format, $types) |
||
3357 | { |
||
3358 | if (!is_array($fields) || !is_array($values) || !is_array($nulls) |
||
3359 | || !is_array($format) || !is_array($types) |
||
3360 | || (count($fields) != count($values)) |
||
3361 | ) { |
||
3362 | return -1; |
||
3363 | } |
||
3364 | |||
3365 | // Build clause |
||
3366 | if (count($values) > 0) { |
||
3367 | // Escape all field names |
||
3368 | $fields = array_map(['\PHPPgAdmin\Database\Postgres', 'fieldClean'], $fields); |
||
3369 | $f_schema = $this->_schema; |
||
3370 | $this->fieldClean($table); |
||
3371 | $this->fieldClean($f_schema); |
||
3372 | |||
3373 | $sql = ''; |
||
3374 | foreach ($values as $i => $value) { |
||
3375 | |||
3376 | // Handle NULL values |
||
3377 | if (isset($nulls[$i])) { |
||
3378 | $sql .= ',NULL'; |
||
3379 | } else { |
||
3380 | $sql .= ',' . $this->formatValue($types[$i], $format[$i], $value); |
||
3381 | } |
||
3382 | } |
||
3383 | |||
3384 | $sql = "INSERT INTO \"{$f_schema}\".\"{$table}\" (\"" . implode('","', $fields) . '") |
||
3385 | VALUES (' . substr($sql, 1) . ')'; |
||
3386 | |||
3387 | return $this->execute($sql); |
||
3388 | } |
||
3389 | |||
3390 | return -1; |
||
3391 | } |
||
3392 | |||
3393 | /** |
||
3394 | * Formats a value or expression for sql purposes |
||
3395 | * |
||
3396 | * @param $type The type of the field |
||
3397 | * @param $format VALUE or EXPRESSION |
||
3398 | * @param $value The actual value entered in the field. Can be NULL |
||
3399 | * @return The suitably quoted and escaped value. |
||
3400 | */ |
||
3401 | public function formatValue($type, $format, $value) |
||
3402 | { |
||
3403 | switch ($type) { |
||
3404 | case 'bool': |
||
3405 | case 'boolean': |
||
3406 | if ($value == 't') { |
||
3407 | return 'TRUE'; |
||
3408 | } |
||
3409 | |||
3410 | if ($value == 'f') { |
||
3411 | return 'FALSE'; |
||
3412 | } elseif ($value == '') { |
||
3413 | return 'NULL'; |
||
3414 | } else { |
||
3415 | return $value; |
||
3416 | } |
||
3417 | |||
3418 | break; |
||
3419 | default: |
||
3420 | // Checking variable fields is difficult as there might be a size |
||
3421 | // attribute... |
||
3422 | if (strpos($type, 'time') === 0) { |
||
3423 | // Assume it's one of the time types... |
||
3424 | if ($value == '') { |
||
3425 | return "''"; |
||
3426 | } |
||
3427 | |||
3428 | if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0 |
||
3429 | || strcasecmp($value, 'CURRENT_TIME') == 0 |
||
3430 | || strcasecmp($value, 'CURRENT_DATE') == 0 |
||
3431 | || strcasecmp($value, 'LOCALTIME') == 0 |
||
3432 | || strcasecmp($value, 'LOCALTIMESTAMP') == 0) { |
||
3433 | return $value; |
||
3434 | } elseif ($format == 'EXPRESSION') { |
||
3435 | return $value; |
||
3436 | } else { |
||
3437 | $this->clean($value); |
||
3438 | |||
3439 | return "'{$value}'"; |
||
3440 | } |
||
3441 | } else { |
||
3442 | if ($format == 'VALUE') { |
||
3443 | $this->clean($value); |
||
3444 | |||
3445 | return "'{$value}'"; |
||
3446 | } |
||
3447 | |||
3448 | return $value; |
||
3449 | } |
||
3450 | } |
||
3451 | } |
||
3452 | |||
3453 | // View functions |
||
3454 | |||
3455 | /** |
||
3456 | * Updates a row in a table |
||
3457 | * |
||
3458 | * @param $table The table in which to update |
||
3459 | * @param $vars An array mapping new values for the row |
||
3460 | * @param $nulls An array mapping column => something if it is to be null |
||
3461 | * @param $format An array of the data type (VALUE or EXPRESSION) |
||
3462 | * @param $types An array of field types |
||
3463 | * @param $keyarr An array mapping column => value to update |
||
3464 | * @return bool|int 0 success |
||
3465 | */ |
||
3466 | public function editRow($table, $vars, $nulls, $format, $types, $keyarr) |
||
3467 | { |
||
3468 | if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) { |
||
3469 | return -1; |
||
3470 | } |
||
3471 | |||
3472 | $f_schema = $this->_schema; |
||
3473 | $this->fieldClean($f_schema); |
||
3474 | $this->fieldClean($table); |
||
3475 | |||
3476 | // Build clause |
||
3477 | if (sizeof($vars) > 0) { |
||
3478 | foreach ($vars as $key => $value) { |
||
3479 | $this->fieldClean($key); |
||
3480 | |||
3481 | // Handle NULL values |
||
3482 | if (isset($nulls[$key])) { |
||
3483 | $tmp = 'NULL'; |
||
3484 | } else { |
||
3485 | $tmp = $this->formatValue($types[$key], $format[$key], $value); |
||
3486 | } |
||
3487 | |||
3488 | if (isset($sql)) { |
||
3489 | $sql .= ", \"{$key}\"={$tmp}"; |
||
3490 | } else { |
||
3491 | $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}"; |
||
3492 | } |
||
3493 | } |
||
3494 | $first = true; |
||
3495 | foreach ($keyarr as $k => $v) { |
||
3496 | $this->fieldClean($k); |
||
3497 | $this->clean($v); |
||
3498 | if ($first) { |
||
3499 | $sql .= " WHERE \"{$k}\"='{$v}'"; |
||
3500 | $first = false; |
||
3501 | } else { |
||
3502 | $sql .= " AND \"{$k}\"='{$v}'"; |
||
3503 | } |
||
3504 | } |
||
3505 | } |
||
3506 | |||
3507 | // Begin transaction. We do this so that we can ensure only one row is |
||
3508 | // edited |
||
3509 | $status = $this->beginTransaction(); |
||
3510 | if ($status != 0) { |
||
3511 | $this->rollbackTransaction(); |
||
3512 | |||
3513 | return -1; |
||
3514 | } |
||
3515 | |||
3516 | $status = $this->execute($sql); |
||
3517 | if ($status != 0) { |
||
3518 | // update failed |
||
3519 | $this->rollbackTransaction(); |
||
3520 | |||
3521 | return -1; |
||
3522 | } |
||
3523 | |||
3524 | if ($this->conn->Affected_Rows() != 1) { |
||
3525 | // more than one row could be updated |
||
3526 | $this->rollbackTransaction(); |
||
3527 | |||
3528 | return -2; |
||
3529 | } |
||
3530 | |||
3531 | // End transaction |
||
3532 | return $this->endTransaction(); |
||
3533 | } |
||
3534 | |||
3535 | /** |
||
3536 | * Delete a row from a table |
||
3537 | * |
||
3538 | * @param $table The table from which to delete |
||
3539 | * @param $key An array mapping column => value to delete |
||
3540 | * @param bool $schema |
||
3541 | * @return bool|int 0 success |
||
3542 | */ |
||
3543 | public function deleteRow($table, $key, $schema = false) |
||
3544 | { |
||
3545 | if (!is_array($key)) { |
||
3546 | return -1; |
||
3547 | } |
||
3548 | |||
3549 | // Begin transaction. We do this so that we can ensure only one row is |
||
3550 | // deleted |
||
3551 | $status = $this->beginTransaction(); |
||
3552 | if ($status != 0) { |
||
3553 | $this->rollbackTransaction(); |
||
3554 | |||
3555 | return -1; |
||
3556 | } |
||
3557 | |||
3558 | if ($schema === false) { |
||
3559 | $schema = $this->_schema; |
||
3560 | } |
||
3561 | |||
3562 | $status = $this->delete($table, $key, $schema); |
||
3563 | if ($status != 0 || $this->conn->Affected_Rows() != 1) { |
||
3564 | $this->rollbackTransaction(); |
||
3565 | |||
3566 | return -2; |
||
3567 | } |
||
3568 | |||
3569 | // End transaction |
||
3570 | return $this->endTransaction(); |
||
3571 | } |
||
3572 | |||
3573 | /** |
||
3574 | * Returns all sequences in the current database |
||
3575 | * |
||
3576 | * @param bool $all |
||
3577 | * @return \PHPPgAdmin\Database\A recordset |
||
3578 | */ |
||
3579 | public function getSequences($all = false) |
||
3580 | { |
||
3581 | if ($all) { |
||
3582 | // Exclude pg_catalog and information_schema tables |
||
3583 | $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner |
||
3584 | FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n |
||
3585 | WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid |
||
3586 | AND c.relkind = 'S' |
||
3587 | AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') |
||
3588 | ORDER BY nspname, seqname"; |
||
3589 | } else { |
||
3590 | $c_schema = $this->_schema; |
||
3591 | $this->clean($c_schema); |
||
3592 | $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment, |
||
3593 | (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace |
||
3594 | FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n |
||
3595 | WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid |
||
3596 | AND c.relkind = 'S' AND n.nspname='{$c_schema}' ORDER BY seqname"; |
||
3597 | } |
||
3598 | |||
3599 | return $this->selectSet($sql); |
||
3600 | } |
||
3601 | |||
3602 | /** |
||
3603 | * Execute nextval on a given sequence |
||
3604 | * |
||
3605 | * @param $sequence Sequence name |
||
3606 | * @return \PHPPgAdmin\Database\A 0 success |
||
3607 | */ |
||
3608 | public function nextvalSequence($sequence) |
||
3609 | { |
||
3610 | /* This double-cleaning is deliberate */ |
||
3611 | $f_schema = $this->_schema; |
||
3612 | $this->fieldClean($f_schema); |
||
3613 | $this->clean($f_schema); |
||
3614 | $this->fieldClean($sequence); |
||
3615 | $this->clean($sequence); |
||
3616 | |||
3617 | $sql = "SELECT pg_catalog.NEXTVAL('\"{$f_schema}\".\"{$sequence}\"')"; |
||
3618 | |||
3619 | return $this->execute($sql); |
||
3620 | } |
||
3621 | |||
3622 | /** |
||
3623 | * Execute setval on a given sequence |
||
3624 | * |
||
3625 | * @param $sequence Sequence name |
||
3626 | * @param $nextvalue The next value |
||
3627 | * @return \PHPPgAdmin\Database\A 0 success |
||
3628 | */ |
||
3629 | public function setvalSequence($sequence, $nextvalue) |
||
3630 | { |
||
3631 | /* This double-cleaning is deliberate */ |
||
3632 | $f_schema = $this->_schema; |
||
3633 | $this->fieldClean($f_schema); |
||
3634 | $this->clean($f_schema); |
||
3635 | $this->fieldClean($sequence); |
||
3636 | $this->clean($sequence); |
||
3637 | $this->clean($nextvalue); |
||
3638 | |||
3639 | $sql = "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', '{$nextvalue}')"; |
||
3640 | |||
3641 | return $this->execute($sql); |
||
3642 | } |
||
3643 | |||
3644 | /** |
||
3645 | * Restart a given sequence to its start value |
||
3646 | * |
||
3647 | * @param $sequence Sequence name |
||
3648 | * @return \PHPPgAdmin\Database\A 0 success |
||
3649 | */ |
||
3650 | View Code Duplication | public function restartSequence($sequence) |
|
3651 | { |
||
3652 | $f_schema = $this->_schema; |
||
3653 | $this->fieldClean($f_schema); |
||
3654 | $this->fieldClean($sequence); |
||
3655 | |||
3656 | $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$sequence}\" RESTART;"; |
||
3657 | |||
3658 | return $this->execute($sql); |
||
3659 | } |
||
3660 | |||
3661 | /** |
||
3662 | * Resets a given sequence to min value of sequence |
||
3663 | * |
||
3664 | * @param $sequence Sequence name |
||
3665 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
3666 | */ |
||
3667 | public function resetSequence($sequence) |
||
3668 | { |
||
3669 | // Get the minimum value of the sequence |
||
3670 | $seq = $this->getSequence($sequence); |
||
3671 | if ($seq->recordCount() != 1) { |
||
3672 | return -1; |
||
3673 | } |
||
3674 | |||
3675 | $minvalue = $seq->fields['min_value']; |
||
3676 | |||
3677 | $f_schema = $this->_schema; |
||
3678 | $this->fieldClean($f_schema); |
||
3679 | /* This double-cleaning is deliberate */ |
||
3680 | $this->fieldClean($sequence); |
||
3681 | $this->clean($sequence); |
||
3682 | |||
3683 | $sql = "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', {$minvalue})"; |
||
3684 | |||
3685 | return $this->execute($sql); |
||
3686 | } |
||
3687 | |||
3688 | /** |
||
3689 | * Returns properties of a single sequence |
||
3690 | * |
||
3691 | * @param $sequence Sequence name |
||
3692 | * @return A recordset |
||
3693 | */ |
||
3694 | public function getSequence($sequence) |
||
3695 | { |
||
3696 | $c_schema = $this->_schema; |
||
3697 | $this->clean($c_schema); |
||
3698 | $c_sequence = $sequence; |
||
3699 | $this->fieldClean($sequence); |
||
3700 | $this->clean($c_sequence); |
||
3701 | |||
3702 | $sql = " |
||
3703 | SELECT c.relname AS seqname, s.*, |
||
3704 | pg_catalog.obj_description(s.tableoid, 'pg_class') AS seqcomment, |
||
3705 | u.usename AS seqowner, n.nspname |
||
3706 | FROM \"{$sequence}\" AS s, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n |
||
3707 | WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid |
||
3708 | AND c.relname = '{$c_sequence}' AND c.relkind = 'S' AND n.nspname='{$c_schema}' |
||
3709 | AND n.oid = c.relnamespace"; |
||
3710 | |||
3711 | return $this->selectSet($sql); |
||
3712 | } |
||
3713 | |||
3714 | /** |
||
3715 | * Creates a new sequence |
||
3716 | * |
||
3717 | * @param $sequence Sequence name |
||
3718 | * @param $increment The increment |
||
3719 | * @param $minvalue The min value |
||
3720 | * @param $maxvalue The max value |
||
3721 | * @param $startvalue The starting value |
||
3722 | * @param $cachevalue The cache value |
||
3723 | * @param $cycledvalue True if cycled, false otherwise |
||
3724 | * @return \PHPPgAdmin\Database\A 0 success |
||
3725 | */ |
||
3726 | public function createSequence( |
||
3727 | $sequence, |
||
3728 | $increment, |
||
3729 | $minvalue, |
||
3730 | $maxvalue, |
||
3731 | $startvalue, |
||
3732 | $cachevalue, |
||
3733 | $cycledvalue |
||
3734 | ) { |
||
3735 | $f_schema = $this->_schema; |
||
3736 | $this->fieldClean($f_schema); |
||
3737 | $this->fieldClean($sequence); |
||
3738 | $this->clean($increment); |
||
3739 | $this->clean($minvalue); |
||
3740 | $this->clean($maxvalue); |
||
3741 | $this->clean($startvalue); |
||
3742 | $this->clean($cachevalue); |
||
3743 | |||
3744 | $sql = "CREATE SEQUENCE \"{$f_schema}\".\"{$sequence}\""; |
||
3745 | if ($increment != '') { |
||
3746 | $sql .= " INCREMENT {$increment}"; |
||
3747 | } |
||
3748 | |||
3749 | if ($minvalue != '') { |
||
3750 | $sql .= " MINVALUE {$minvalue}"; |
||
3751 | } |
||
3752 | |||
3753 | if ($maxvalue != '') { |
||
3754 | $sql .= " MAXVALUE {$maxvalue}"; |
||
3755 | } |
||
3756 | |||
3757 | if ($startvalue != '') { |
||
3758 | $sql .= " START {$startvalue}"; |
||
3759 | } |
||
3760 | |||
3761 | if ($cachevalue != '') { |
||
3762 | $sql .= " CACHE {$cachevalue}"; |
||
3763 | } |
||
3764 | |||
3765 | if ($cycledvalue) { |
||
3766 | $sql .= ' CYCLE'; |
||
3767 | } |
||
3768 | |||
3769 | return $this->execute($sql); |
||
3770 | } |
||
3771 | |||
3772 | /** |
||
3773 | * Alters a sequence |
||
3774 | * |
||
3775 | * @param $sequence The name of the sequence |
||
3776 | * @param $name The new name for the sequence |
||
3777 | * @param $comment The comment on the sequence |
||
3778 | * @param $owner The new owner for the sequence |
||
3779 | * @param $schema The new schema for the sequence |
||
3780 | * @param $increment The increment |
||
3781 | * @param $minvalue The min value |
||
3782 | * @param $maxvalue The max value |
||
3783 | * @param $restartvalue The starting value |
||
3784 | * @param $cachevalue The cache value |
||
3785 | * @param $cycledvalue True if cycled, false otherwise |
||
3786 | * @param $startvalue The sequence start value when issueing a restart |
||
3787 | * @return bool|int 0 success |
||
3788 | */ |
||
3789 | public function alterSequence( |
||
3790 | $sequence, |
||
3791 | $name, |
||
3792 | $comment, |
||
3793 | $owner = null, |
||
3794 | $schema = null, |
||
3795 | $increment = null, |
||
3796 | $minvalue = null, |
||
3797 | $maxvalue = null, |
||
3798 | $restartvalue = null, |
||
3799 | $cachevalue = null, |
||
3800 | $cycledvalue = null, |
||
3801 | $startvalue = null |
||
3802 | ) { |
||
3803 | $this->fieldClean($sequence); |
||
3804 | |||
3805 | $data = $this->getSequence($sequence); |
||
3806 | |||
3807 | if ($data->recordCount() != 1) { |
||
3808 | return -2; |
||
3809 | } |
||
3810 | |||
3811 | $status = $this->beginTransaction(); |
||
3812 | if ($status != 0) { |
||
3813 | $this->rollbackTransaction(); |
||
3814 | |||
3815 | return -1; |
||
3816 | } |
||
3817 | |||
3818 | $status = $this->_alterSequence($data, $name, $comment, $owner, $schema, $increment, |
||
3819 | $minvalue, $maxvalue, $restartvalue, $cachevalue, $cycledvalue, $startvalue); |
||
3820 | |||
3821 | if ($status != 0) { |
||
3822 | $this->rollbackTransaction(); |
||
3823 | |||
3824 | return $status; |
||
3825 | } |
||
3826 | |||
3827 | return $this->endTransaction(); |
||
3828 | } |
||
3829 | |||
3830 | /** |
||
3831 | * Protected method which alter a sequence |
||
3832 | * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION |
||
3833 | * |
||
3834 | * @param $seqrs The sequence recordSet returned by getSequence() |
||
3835 | * @param $name The new name for the sequence |
||
3836 | * @param $comment The comment on the sequence |
||
3837 | * @param $owner The new owner for the sequence |
||
3838 | * @param $schema The new schema for the sequence |
||
3839 | * @param $increment The increment |
||
3840 | * @param $minvalue The min value |
||
3841 | * @param $maxvalue The max value |
||
3842 | * @param $restartvalue The starting value |
||
3843 | * @param $cachevalue The cache value |
||
3844 | * @param $cycledvalue True if cycled, false otherwise |
||
3845 | * @param $startvalue The sequence start value when issueing a restart |
||
3846 | * @return int 0 success |
||
3847 | */ |
||
3848 | protected function _alterSequence( |
||
3849 | $seqrs, |
||
3850 | $name, |
||
3851 | $comment, |
||
3852 | $owner, |
||
3853 | $schema, |
||
3854 | $increment, |
||
3855 | $minvalue, |
||
3856 | $maxvalue, |
||
3857 | $restartvalue, |
||
3858 | $cachevalue, |
||
3859 | $cycledvalue, |
||
3860 | $startvalue |
||
3861 | ) { |
||
3862 | $this->fieldArrayClean($seqrs->fields); |
||
3863 | |||
3864 | // Comment |
||
3865 | $status = $this->setComment('SEQUENCE', $seqrs->fields['seqname'], '', $comment); |
||
3866 | if ($status != 0) { |
||
3867 | return -4; |
||
3868 | } |
||
3869 | |||
3870 | // Owner |
||
3871 | $this->fieldClean($owner); |
||
3872 | $status = $this->alterSequenceOwner($seqrs, $owner); |
||
3873 | if ($status != 0) { |
||
3874 | return -5; |
||
3875 | } |
||
3876 | |||
3877 | // Props |
||
3878 | $this->clean($increment); |
||
3879 | $this->clean($minvalue); |
||
3880 | $this->clean($maxvalue); |
||
3881 | $this->clean($restartvalue); |
||
3882 | $this->clean($cachevalue); |
||
3883 | $this->clean($cycledvalue); |
||
3884 | $this->clean($startvalue); |
||
3885 | $status = $this->alterSequenceProps($seqrs, $increment, $minvalue, |
||
3886 | $maxvalue, $restartvalue, $cachevalue, $cycledvalue, $startvalue); |
||
3887 | if ($status != 0) { |
||
3888 | return -6; |
||
3889 | } |
||
3890 | |||
3891 | // Rename |
||
3892 | $this->fieldClean($name); |
||
3893 | $status = $this->alterSequenceName($seqrs, $name); |
||
3894 | if ($status != 0) { |
||
3895 | return -3; |
||
3896 | } |
||
3897 | |||
3898 | // Schema |
||
3899 | $this->clean($schema); |
||
3900 | $status = $this->alterSequenceSchema($seqrs, $schema); |
||
3901 | if ($status != 0) { |
||
3902 | return -7; |
||
3903 | } |
||
3904 | |||
3905 | return 0; |
||
3906 | } |
||
3907 | |||
3908 | // Index functions |
||
3909 | |||
3910 | /** |
||
3911 | * Alter a sequence's owner |
||
3912 | * |
||
3913 | * @param $seqrs The sequence RecordSet returned by getSequence() |
||
3914 | * @param $owner |
||
3915 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
3916 | * @internal param \PHPPgAdmin\Database\The $name new owner for the sequence |
||
3917 | */ |
||
3918 | View Code Duplication | public function alterSequenceOwner($seqrs, $owner) |
|
3919 | { |
||
3920 | // If owner has been changed, then do the alteration. We are |
||
3921 | // careful to avoid this generally as changing owner is a |
||
3922 | // superuser only function. |
||
3923 | /* vars are cleaned in _alterSequence */ |
||
3924 | if (!empty($owner) && ($seqrs->fields['seqowner'] != $owner)) { |
||
3925 | $f_schema = $this->_schema; |
||
3926 | $this->fieldClean($f_schema); |
||
3927 | $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" OWNER TO \"{$owner}\""; |
||
3928 | |||
3929 | return $this->execute($sql); |
||
3930 | } |
||
3931 | |||
3932 | return 0; |
||
3933 | } |
||
3934 | |||
3935 | /** |
||
3936 | * Alter a sequence's properties |
||
3937 | * |
||
3938 | * @param $seqrs The sequence RecordSet returned by getSequence() |
||
3939 | * @param $increment The sequence incremental value |
||
3940 | * @param $minvalue The sequence minimum value |
||
3941 | * @param $maxvalue The sequence maximum value |
||
3942 | * @param $restartvalue The sequence current value |
||
3943 | * @param $cachevalue The sequence cache value |
||
3944 | * @param $cycledvalue Sequence can cycle ? |
||
3945 | * @param $startvalue The sequence start value when issueing a restart |
||
3946 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
3947 | */ |
||
3948 | public function alterSequenceProps( |
||
3949 | $seqrs, |
||
3950 | $increment, |
||
3951 | $minvalue, |
||
3952 | $maxvalue, |
||
3953 | $restartvalue, |
||
3954 | $cachevalue, |
||
3955 | $cycledvalue, |
||
3956 | $startvalue |
||
3957 | ) { |
||
3958 | $sql = ''; |
||
3959 | /* vars are cleaned in _alterSequence */ |
||
3960 | if (!empty($increment) && ($increment != $seqrs->fields['increment_by'])) { |
||
3961 | $sql .= " INCREMENT {$increment}"; |
||
3962 | } |
||
3963 | |||
3964 | if (!empty($minvalue) && ($minvalue != $seqrs->fields['min_value'])) { |
||
3965 | $sql .= " MINVALUE {$minvalue}"; |
||
3966 | } |
||
3967 | |||
3968 | if (!empty($maxvalue) && ($maxvalue != $seqrs->fields['max_value'])) { |
||
3969 | $sql .= " MAXVALUE {$maxvalue}"; |
||
3970 | } |
||
3971 | |||
3972 | if (!empty($restartvalue) && ($restartvalue != $seqrs->fields['last_value'])) { |
||
3973 | $sql .= " RESTART {$restartvalue}"; |
||
3974 | } |
||
3975 | |||
3976 | if (!empty($cachevalue) && ($cachevalue != $seqrs->fields['cache_value'])) { |
||
3977 | $sql .= " CACHE {$cachevalue}"; |
||
3978 | } |
||
3979 | |||
3980 | if (!empty($startvalue) && ($startvalue != $seqrs->fields['start_value'])) { |
||
3981 | $sql .= " START {$startvalue}"; |
||
3982 | } |
||
3983 | |||
3984 | // toggle cycle yes/no |
||
3985 | if (!is_null($cycledvalue)) { |
||
3986 | $sql .= (!$cycledvalue ? ' NO ' : '') . ' CYCLE'; |
||
3987 | } |
||
3988 | |||
3989 | if ($sql != '') { |
||
3990 | $f_schema = $this->_schema; |
||
3991 | $this->fieldClean($f_schema); |
||
3992 | $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" {$sql}"; |
||
3993 | |||
3994 | return $this->execute($sql); |
||
3995 | } |
||
3996 | |||
3997 | return 0; |
||
3998 | } |
||
3999 | |||
4000 | /** |
||
4001 | * Rename a sequence |
||
4002 | * |
||
4003 | * @param $seqrs The sequence RecordSet returned by getSequence() |
||
4004 | * @param $name The new name for the sequence |
||
4005 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4006 | */ |
||
4007 | View Code Duplication | public function alterSequenceName($seqrs, $name) |
|
4008 | { |
||
4009 | /* vars are cleaned in _alterSequence */ |
||
4010 | if (!empty($name) && ($seqrs->fields['seqname'] != $name)) { |
||
4011 | $f_schema = $this->_schema; |
||
4012 | $this->fieldClean($f_schema); |
||
4013 | $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" RENAME TO \"{$name}\""; |
||
4014 | $status = $this->execute($sql); |
||
4015 | if ($status == 0) { |
||
4016 | $seqrs->fields['seqname'] = $name; |
||
4017 | } else { |
||
4018 | return $status; |
||
4019 | } |
||
4020 | } |
||
4021 | |||
4022 | return 0; |
||
4023 | } |
||
4024 | |||
4025 | /** |
||
4026 | * Alter a sequence's schema |
||
4027 | * |
||
4028 | * @param $seqrs The sequence RecordSet returned by getSequence() |
||
4029 | * @param $schema |
||
4030 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4031 | * @internal param \PHPPgAdmin\Database\The $name new schema for the sequence |
||
4032 | */ |
||
4033 | View Code Duplication | public function alterSequenceSchema($seqrs, $schema) |
|
4034 | { |
||
4035 | /* vars are cleaned in _alterSequence */ |
||
4036 | if (!empty($schema) && ($seqrs->fields['nspname'] != $schema)) { |
||
4037 | $f_schema = $this->_schema; |
||
4038 | $this->fieldClean($f_schema); |
||
4039 | $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" SET SCHEMA {$schema}"; |
||
4040 | |||
4041 | return $this->execute($sql); |
||
4042 | } |
||
4043 | |||
4044 | return 0; |
||
4045 | } |
||
4046 | |||
4047 | /** |
||
4048 | * Drops a given sequence |
||
4049 | * |
||
4050 | * @param $sequence Sequence name |
||
4051 | * @param $cascade True to cascade drop, false to restrict |
||
4052 | * @return \PHPPgAdmin\Database\A 0 success |
||
4053 | */ |
||
4054 | View Code Duplication | public function dropSequence($sequence, $cascade) |
|
4055 | { |
||
4056 | $f_schema = $this->_schema; |
||
4057 | $this->fieldClean($f_schema); |
||
4058 | $this->fieldClean($sequence); |
||
4059 | |||
4060 | $sql = "DROP SEQUENCE \"{$f_schema}\".\"{$sequence}\""; |
||
4061 | if ($cascade) { |
||
4062 | $sql .= ' CASCADE'; |
||
4063 | } |
||
4064 | |||
4065 | return $this->execute($sql); |
||
4066 | } |
||
4067 | |||
4068 | /** |
||
4069 | * Returns a list of all views in the database |
||
4070 | * |
||
4071 | * @return All views |
||
4072 | */ |
||
4073 | public function getViews() |
||
4074 | { |
||
4075 | $c_schema = $this->_schema; |
||
4076 | $this->clean($c_schema); |
||
4077 | $sql = " |
||
4078 | SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, |
||
4079 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment |
||
4080 | FROM pg_catalog.pg_class c |
||
4081 | LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) |
||
4082 | WHERE (n.nspname='{$c_schema}') AND (c.relkind = 'v'::\"char\") |
||
4083 | ORDER BY relname"; |
||
4084 | |||
4085 | return $this->selectSet($sql); |
||
4086 | } |
||
4087 | |||
4088 | // Constraint functions |
||
4089 | |||
4090 | /** |
||
4091 | * Returns a list of all views in the database |
||
4092 | * |
||
4093 | * @return All views |
||
4094 | */ |
||
4095 | public function getMaterializedViews() |
||
4096 | { |
||
4097 | $c_schema = $this->_schema; |
||
4098 | $this->clean($c_schema); |
||
4099 | $sql = " |
||
4100 | SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, |
||
4101 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment |
||
4102 | FROM pg_catalog.pg_class c |
||
4103 | LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) |
||
4104 | WHERE (n.nspname='{$c_schema}') AND (c.relkind = 'm'::\"char\") |
||
4105 | ORDER BY relname"; |
||
4106 | |||
4107 | return $this->selectSet($sql); |
||
4108 | } |
||
4109 | |||
4110 | /** |
||
4111 | * Updates a view. |
||
4112 | * |
||
4113 | * @param $viewname The name fo the view to update |
||
4114 | * @param $definition The new definition for the view |
||
4115 | * @param $comment |
||
4116 | * @return bool|int 0 success |
||
4117 | */ |
||
4118 | public function setView($viewname, $definition, $comment) |
||
4119 | { |
||
4120 | return $this->createView($viewname, $definition, true, $comment); |
||
4121 | } |
||
4122 | |||
4123 | /** |
||
4124 | * Creates a new view. |
||
4125 | * |
||
4126 | * @param $viewname The name of the view to create |
||
4127 | * @param $definition The definition for the new view |
||
4128 | * @param $replace True to replace the view, false otherwise |
||
4129 | * @param $comment |
||
4130 | * @return bool|int 0 success |
||
4131 | */ |
||
4132 | public function createView($viewname, $definition, $replace, $comment) |
||
4133 | { |
||
4134 | $status = $this->beginTransaction(); |
||
4135 | if ($status != 0) { |
||
4136 | return -1; |
||
4137 | } |
||
4138 | |||
4139 | $f_schema = $this->_schema; |
||
4140 | $this->fieldClean($f_schema); |
||
4141 | $this->fieldClean($viewname); |
||
4142 | |||
4143 | // Note: $definition not cleaned |
||
4144 | |||
4145 | $sql = 'CREATE '; |
||
4146 | if ($replace) { |
||
4147 | $sql .= 'OR REPLACE '; |
||
4148 | } |
||
4149 | |||
4150 | $sql .= "VIEW \"{$f_schema}\".\"{$viewname}\" AS {$definition}"; |
||
4151 | |||
4152 | $status = $this->execute($sql); |
||
4153 | if ($status) { |
||
4154 | $this->rollbackTransaction(); |
||
4155 | |||
4156 | return -1; |
||
4157 | } |
||
4158 | |||
4159 | if ($comment != '') { |
||
4160 | $status = $this->setComment('VIEW', $viewname, '', $comment); |
||
4161 | if ($status) { |
||
4162 | $this->rollbackTransaction(); |
||
4163 | |||
4164 | return -1; |
||
4165 | } |
||
4166 | } |
||
4167 | |||
4168 | return $this->endTransaction(); |
||
4169 | } |
||
4170 | |||
4171 | /** |
||
4172 | * Alter view properties |
||
4173 | * |
||
4174 | * @param $view The name of the view |
||
4175 | * @param $name The new name for the view |
||
4176 | * @param $owner The new owner for the view |
||
4177 | * @param $schema The new schema for the view |
||
4178 | * @param $comment The comment on the view |
||
4179 | * @return bool|int 0 success |
||
4180 | */ |
||
4181 | View Code Duplication | public function alterView($view, $name, $owner, $schema, $comment) |
|
4182 | { |
||
4183 | $data = $this->getView($view); |
||
4184 | if ($data->recordCount() != 1) { |
||
4185 | return -2; |
||
4186 | } |
||
4187 | |||
4188 | $status = $this->beginTransaction(); |
||
4189 | if ($status != 0) { |
||
4190 | $this->rollbackTransaction(); |
||
4191 | |||
4192 | return -1; |
||
4193 | } |
||
4194 | |||
4195 | $status = $this->_alterView($data, $name, $owner, $schema, $comment); |
||
4196 | |||
4197 | if ($status != 0) { |
||
4198 | $this->rollbackTransaction(); |
||
4199 | |||
4200 | return $status; |
||
4201 | } |
||
4202 | |||
4203 | return $this->endTransaction(); |
||
4204 | } |
||
4205 | |||
4206 | /** |
||
4207 | * Returns all details for a particular view |
||
4208 | * |
||
4209 | * @param $view The name of the view to retrieve |
||
4210 | * @return View info |
||
4211 | */ |
||
4212 | public function getView($view) |
||
4213 | { |
||
4214 | $c_schema = $this->_schema; |
||
4215 | $this->clean($c_schema); |
||
4216 | $this->clean($view); |
||
4217 | |||
4218 | $sql = " |
||
4219 | SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, |
||
4220 | pg_catalog.pg_get_viewdef(c.oid, true) AS vwdefinition, |
||
4221 | pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment |
||
4222 | FROM pg_catalog.pg_class c |
||
4223 | LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace) |
||
4224 | WHERE (c.relname = '{$view}') AND n.nspname='{$c_schema}'"; |
||
4225 | |||
4226 | return $this->selectSet($sql); |
||
4227 | } |
||
4228 | |||
4229 | /** |
||
4230 | * Protected method which alter a view |
||
4231 | * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION |
||
4232 | * |
||
4233 | * @param $vwrs The view recordSet returned by getView() |
||
4234 | * @param $name The new name for the view |
||
4235 | * @param $owner The new owner for the view |
||
4236 | * @param $schema |
||
4237 | * @param $comment The comment on the view |
||
4238 | * @return int 0 success |
||
4239 | */ |
||
4240 | View Code Duplication | protected function _alterView($vwrs, $name, $owner, $schema, $comment) |
|
4241 | { |
||
4242 | $this->fieldArrayClean($vwrs->fields); |
||
4243 | |||
4244 | // Comment |
||
4245 | if ($this->setComment('VIEW', $vwrs->fields['relname'], '', $comment) != 0) { |
||
4246 | return -4; |
||
4247 | } |
||
4248 | |||
4249 | // Owner |
||
4250 | $this->fieldClean($owner); |
||
4251 | $status = $this->alterViewOwner($vwrs, $owner); |
||
4252 | if ($status != 0) { |
||
4253 | return -5; |
||
4254 | } |
||
4255 | |||
4256 | // Rename |
||
4257 | $this->fieldClean($name); |
||
4258 | $status = $this->alterViewName($vwrs, $name); |
||
4259 | if ($status != 0) { |
||
4260 | return -3; |
||
4261 | } |
||
4262 | |||
4263 | // Schema |
||
4264 | $this->fieldClean($schema); |
||
4265 | $status = $this->alterViewSchema($vwrs, $schema); |
||
4266 | if ($status != 0) { |
||
4267 | return -6; |
||
4268 | } |
||
4269 | |||
4270 | return 0; |
||
4271 | } |
||
4272 | |||
4273 | /** |
||
4274 | * Alter a view's owner |
||
4275 | * |
||
4276 | * @param $vwrs The view recordSet returned by getView() |
||
4277 | * @param null $owner |
||
4278 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4279 | * @internal param \PHPPgAdmin\Database\The $name new view's owner |
||
4280 | */ |
||
4281 | View Code Duplication | public function alterViewOwner($vwrs, $owner = null) |
|
4282 | { |
||
4283 | /* $vwrs and $owner are cleaned in _alterView */ |
||
4284 | if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) { |
||
4285 | $f_schema = $this->_schema; |
||
4286 | $this->fieldClean($f_schema); |
||
4287 | // If owner has been changed, then do the alteration. We are |
||
4288 | // careful to avoid this generally as changing owner is a |
||
4289 | // superuser only function. |
||
4290 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$vwrs->fields['relname']}\" OWNER TO \"{$owner}\""; |
||
4291 | |||
4292 | return $this->execute($sql); |
||
4293 | } |
||
4294 | |||
4295 | return 0; |
||
4296 | } |
||
4297 | |||
4298 | /** |
||
4299 | * Rename a view |
||
4300 | * |
||
4301 | * @param $vwrs The view recordSet returned by getView() |
||
4302 | * @param $name The new view's name |
||
4303 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4304 | */ |
||
4305 | View Code Duplication | public function alterViewName($vwrs, $name) |
|
4306 | { |
||
4307 | // Rename (only if name has changed) |
||
4308 | /* $vwrs and $name are cleaned in _alterView */ |
||
4309 | if (!empty($name) && ($name != $vwrs->fields['relname'])) { |
||
4310 | $f_schema = $this->_schema; |
||
4311 | $this->fieldClean($f_schema); |
||
4312 | $sql = "ALTER VIEW \"{$f_schema}\".\"{$vwrs->fields['relname']}\" RENAME TO \"{$name}\""; |
||
4313 | $status = $this->execute($sql); |
||
4314 | if ($status == 0) { |
||
4315 | $vwrs->fields['relname'] = $name; |
||
4316 | } else { |
||
4317 | return $status; |
||
4318 | } |
||
4319 | } |
||
4320 | |||
4321 | return 0; |
||
4322 | } |
||
4323 | |||
4324 | /** |
||
4325 | * Alter a view's schema |
||
4326 | * |
||
4327 | * @param $vwrs The view recordSet returned by getView() |
||
4328 | * @param $schema |
||
4329 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4330 | * @internal param \PHPPgAdmin\Database\The $name new view's schema |
||
4331 | */ |
||
4332 | View Code Duplication | public function alterViewSchema($vwrs, $schema) |
|
4333 | { |
||
4334 | /* $vwrs and $schema are cleaned in _alterView */ |
||
4335 | if (!empty($schema) && ($vwrs->fields['nspname'] != $schema)) { |
||
4336 | $f_schema = $this->_schema; |
||
4337 | $this->fieldClean($f_schema); |
||
4338 | // If tablespace has been changed, then do the alteration. We |
||
4339 | // don't want to do this unnecessarily. |
||
4340 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$vwrs->fields['relname']}\" SET SCHEMA \"{$schema}\""; |
||
4341 | |||
4342 | return $this->execute($sql); |
||
4343 | } |
||
4344 | |||
4345 | return 0; |
||
4346 | } |
||
4347 | |||
4348 | /** |
||
4349 | * Drops a view. |
||
4350 | * |
||
4351 | * @param $viewname The name of the view to drop |
||
4352 | * @param $cascade True to cascade drop, false to restrict |
||
4353 | * @return \PHPPgAdmin\Database\A 0 success |
||
4354 | */ |
||
4355 | View Code Duplication | public function dropView($viewname, $cascade) |
|
4356 | { |
||
4357 | $f_schema = $this->_schema; |
||
4358 | $this->fieldClean($f_schema); |
||
4359 | $this->fieldClean($viewname); |
||
4360 | |||
4361 | $sql = "DROP VIEW \"{$f_schema}\".\"{$viewname}\""; |
||
4362 | if ($cascade) { |
||
4363 | $sql .= ' CASCADE'; |
||
4364 | } |
||
4365 | |||
4366 | return $this->execute($sql); |
||
4367 | } |
||
4368 | |||
4369 | // Domain functions |
||
4370 | |||
4371 | /** |
||
4372 | * test if a table has been clustered on an index |
||
4373 | * |
||
4374 | * @param $table The table to test |
||
4375 | * |
||
4376 | * @return true if the table has been already clustered |
||
4377 | */ |
||
4378 | View Code Duplication | public function alreadyClustered($table) |
|
4379 | { |
||
4380 | $c_schema = $this->_schema; |
||
4381 | $this->clean($c_schema); |
||
4382 | $this->clean($table); |
||
4383 | |||
4384 | $sql = "SELECT i.indisclustered |
||
4385 | FROM pg_catalog.pg_class c, pg_catalog.pg_index i |
||
4386 | WHERE c.relname = '{$table}' |
||
4387 | AND c.oid = i.indrelid AND i.indisclustered |
||
4388 | AND c.relnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||
4389 | WHERE nspname='{$c_schema}') |
||
4390 | "; |
||
4391 | |||
4392 | $v = $this->selectSet($sql); |
||
4393 | |||
4394 | return !($v->recordCount() == 0); |
||
4395 | } |
||
4396 | |||
4397 | /** |
||
4398 | * Creates an index |
||
4399 | * |
||
4400 | * @param $name The index name |
||
4401 | * @param $table The table on which to add the index |
||
4402 | * @param $columns An array of columns that form the index |
||
4403 | * or a string expression for a functional index |
||
4404 | * @param $type The index type |
||
4405 | * @param $unique True if unique, false otherwise |
||
4406 | * @param $where Index predicate ('' for none) |
||
4407 | * @param $tablespace The tablespaces ('' means none/default) |
||
4408 | * @param $concurrently |
||
4409 | * @return \PHPPgAdmin\Database\A 0 success |
||
4410 | */ |
||
4411 | public function createIndex($name, $table, $columns, $type, $unique, $where, $tablespace, $concurrently) |
||
4412 | { |
||
4413 | $f_schema = $this->_schema; |
||
4414 | $this->fieldClean($f_schema); |
||
4415 | $this->fieldClean($name); |
||
4416 | $this->fieldClean($table); |
||
4417 | |||
4418 | $sql = 'CREATE'; |
||
4419 | if ($unique) { |
||
4420 | $sql .= ' UNIQUE'; |
||
4421 | } |
||
4422 | |||
4423 | $sql .= ' INDEX'; |
||
4424 | if ($concurrently) { |
||
4425 | $sql .= ' CONCURRENTLY'; |
||
4426 | } |
||
4427 | |||
4428 | $sql .= " \"{$name}\" ON \"{$f_schema}\".\"{$table}\" USING {$type} "; |
||
4429 | |||
4430 | if (is_array($columns)) { |
||
4431 | $this->arrayClean($columns); |
||
4432 | $sql .= '("' . implode('","', $columns) . '")'; |
||
4433 | } else { |
||
4434 | $sql .= '(' . $columns . ')'; |
||
4435 | } |
||
4436 | |||
4437 | // Tablespace |
||
4438 | if ($this->hasTablespaces() && $tablespace != '') { |
||
4439 | $this->fieldClean($tablespace); |
||
4440 | $sql .= " TABLESPACE \"{$tablespace}\""; |
||
4441 | } |
||
4442 | |||
4443 | // Predicate |
||
4444 | if (trim($where) != '') { |
||
4445 | $sql .= " WHERE ({$where})"; |
||
4446 | } |
||
4447 | |||
4448 | return $this->execute($sql); |
||
4449 | } |
||
4450 | |||
4451 | /** |
||
4452 | * Removes an index from the database |
||
4453 | * |
||
4454 | * @param $index The index to drop |
||
4455 | * @param $cascade True to cascade drop, false to restrict |
||
4456 | * @return \PHPPgAdmin\Database\A 0 success |
||
4457 | */ |
||
4458 | View Code Duplication | public function dropIndex($index, $cascade) |
|
4459 | { |
||
4460 | $f_schema = $this->_schema; |
||
4461 | $this->fieldClean($f_schema); |
||
4462 | $this->fieldClean($index); |
||
4463 | |||
4464 | $sql = "DROP INDEX \"{$f_schema}\".\"{$index}\""; |
||
4465 | if ($cascade) { |
||
4466 | $sql .= ' CASCADE'; |
||
4467 | } |
||
4468 | |||
4469 | return $this->execute($sql); |
||
4470 | } |
||
4471 | |||
4472 | /** |
||
4473 | * Rebuild indexes |
||
4474 | * |
||
4475 | * @param $type 'DATABASE' or 'TABLE' or 'INDEX' |
||
4476 | * @param $name The name of the specific database, table, or index to be reindexed |
||
4477 | * @param bool|\PHPPgAdmin\Database\If $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in |
||
4478 | * 7.2-7.3, ignored in >=7.4 |
||
4479 | * @return int|\PHPPgAdmin\Database\A |
||
4480 | */ |
||
4481 | public function reindex($type, $name, $force = false) |
||
4482 | { |
||
4483 | $f_schema = $this->_schema; |
||
4484 | $this->fieldClean($f_schema); |
||
4485 | $this->fieldClean($name); |
||
4486 | switch ($type) { |
||
4487 | case 'DATABASE': |
||
4488 | $sql = "REINDEX {$type} \"{$name}\""; |
||
4489 | if ($force) { |
||
4490 | $sql .= ' FORCE'; |
||
4491 | } |
||
4492 | |||
4493 | break; |
||
4494 | case 'TABLE': |
||
4495 | case 'INDEX': |
||
4496 | $sql = "REINDEX {$type} \"{$f_schema}\".\"{$name}\""; |
||
4497 | if ($force) { |
||
4498 | $sql .= ' FORCE'; |
||
4499 | } |
||
4500 | |||
4501 | break; |
||
4502 | default: |
||
4503 | return -1; |
||
4504 | } |
||
4505 | |||
4506 | return $this->execute($sql); |
||
4507 | } |
||
4508 | |||
4509 | /** |
||
4510 | * Clusters an index |
||
4511 | * |
||
4512 | * @param \PHPPgAdmin\Database\The|string $table The table the index is on |
||
4513 | * @param \PHPPgAdmin\Database\The|string $index The name of the index |
||
4514 | * @return \PHPPgAdmin\Database\A 0 success |
||
4515 | */ |
||
4516 | View Code Duplication | public function clusterIndex($table = '', $index = '') |
|
4517 | { |
||
4518 | $sql = 'CLUSTER'; |
||
4519 | |||
4520 | // We don't bother with a transaction here, as there's no point rolling |
||
4521 | // back an expensive cluster if a cheap analyze fails for whatever reason |
||
4522 | |||
4523 | if (!empty($table)) { |
||
4524 | $f_schema = $this->_schema; |
||
4525 | $this->fieldClean($f_schema); |
||
4526 | $this->fieldClean($table); |
||
4527 | $sql .= " \"{$f_schema}\".\"{$table}\""; |
||
4528 | |||
4529 | if (!empty($index)) { |
||
4530 | $this->fieldClean($index); |
||
4531 | $sql .= " USING \"{$index}\""; |
||
4532 | } |
||
4533 | } |
||
4534 | |||
4535 | return $this->execute($sql); |
||
4536 | } |
||
4537 | |||
4538 | /** |
||
4539 | * Returns a list of all constraints on a table, |
||
4540 | * including constraint name, definition, related col and referenced namespace, |
||
4541 | * table and col if needed |
||
4542 | * |
||
4543 | * @param $table the table where we are looking for fk |
||
4544 | * @return a recordset |
||
4545 | */ |
||
4546 | View Code Duplication | public function getConstraintsWithFields($table) |
|
4547 | { |
||
4548 | $c_schema = $this->_schema; |
||
4549 | $this->clean($c_schema); |
||
4550 | $this->clean($table); |
||
4551 | |||
4552 | // get the max number of col used in a constraint for the table |
||
4553 | $sql = "SELECT DISTINCT |
||
4554 | max(SUBSTRING(array_dims(c.conkey) FROM \$patern\$^\\[.*:(.*)\\]$\$patern\$)) as nb |
||
4555 | FROM pg_catalog.pg_constraint AS c |
||
4556 | JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid) |
||
4557 | JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid) |
||
4558 | WHERE |
||
4559 | r.relname = '{$table}' AND ns.nspname='{$c_schema}'"; |
||
4560 | |||
4561 | $rs = $this->selectSet($sql); |
||
4562 | |||
4563 | if ($rs->EOF) { |
||
4564 | $max_col = 0; |
||
4565 | } else { |
||
4566 | $max_col = $rs->fields['nb']; |
||
4567 | } |
||
4568 | |||
4569 | $sql = ' |
||
4570 | SELECT |
||
4571 | c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc, |
||
4572 | ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema, |
||
4573 | r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field, |
||
4574 | f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment, |
||
4575 | c.conrelid, c.confrelid |
||
4576 | FROM |
||
4577 | pg_catalog.pg_constraint AS c |
||
4578 | JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid) |
||
4579 | JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]'; |
||
4580 | for ($i = 2; $i <= $rs->fields['nb']; $i++) { |
||
4581 | $sql .= " OR f1.attnum=c.conkey[$i]"; |
||
4582 | } |
||
4583 | $sql .= ')) |
||
4584 | JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid |
||
4585 | LEFT JOIN ( |
||
4586 | pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid) |
||
4587 | ) ON (c.confrelid=r2.oid) |
||
4588 | LEFT JOIN pg_catalog.pg_attribute AS f2 ON |
||
4589 | (f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)'; |
||
4590 | for ($i = 2; $i <= $rs->fields['nb']; $i++) { |
||
4591 | $sql .= " OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)"; |
||
4592 | } |
||
4593 | |||
4594 | $sql .= sprintf(")) |
||
4595 | WHERE |
||
4596 | r1.relname = '%s' AND ns1.nspname='%s' |
||
4597 | ORDER BY 1", $table, $c_schema); |
||
4598 | |||
4599 | return $this->selectSet($sql); |
||
4600 | } |
||
4601 | |||
4602 | /** |
||
4603 | * Adds a primary key constraint to a table |
||
4604 | * |
||
4605 | * @param $table The table to which to add the primery key |
||
4606 | * @param $fields (array) An array of fields over which to add the primary key |
||
4607 | * @param string $name (optional) The name to give the key, otherwise default name is assigned |
||
4608 | * @param string $tablespace (optional) The tablespace for the schema, '' indicates default. |
||
4609 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4610 | */ |
||
4611 | View Code Duplication | public function addPrimaryKey($table, $fields, $name = '', $tablespace = '') |
|
4612 | { |
||
4613 | if (!is_array($fields) || sizeof($fields) == 0) { |
||
4614 | return -1; |
||
4615 | } |
||
4616 | |||
4617 | $f_schema = $this->_schema; |
||
4618 | $this->fieldClean($f_schema); |
||
4619 | $this->fieldClean($table); |
||
4620 | $this->fieldArrayClean($fields); |
||
4621 | $this->fieldClean($name); |
||
4622 | $this->fieldClean($tablespace); |
||
4623 | |||
4624 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD "; |
||
4625 | if ($name != '') { |
||
4626 | $sql .= "CONSTRAINT \"{$name}\" "; |
||
4627 | } |
||
4628 | |||
4629 | $sql .= 'PRIMARY KEY ("' . join('","', $fields) . '")'; |
||
4630 | |||
4631 | if ($tablespace != '' && $this->hasTablespaces()) { |
||
4632 | $sql .= " USING INDEX TABLESPACE \"{$tablespace}\""; |
||
4633 | } |
||
4634 | |||
4635 | return $this->execute($sql); |
||
4636 | } |
||
4637 | |||
4638 | /** |
||
4639 | * Adds a unique constraint to a table |
||
4640 | * |
||
4641 | * @param $table The table to which to add the unique key |
||
4642 | * @param $fields (array) An array of fields over which to add the unique key |
||
4643 | * @param string $name (optional) The name to give the key, otherwise default name is assigned |
||
4644 | * @param string $tablespace (optional) The tablespace for the schema, '' indicates default. |
||
4645 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
4646 | */ |
||
4647 | View Code Duplication | public function addUniqueKey($table, $fields, $name = '', $tablespace = '') |
|
4648 | { |
||
4649 | if (!is_array($fields) || sizeof($fields) == 0) { |
||
4650 | return -1; |
||
4651 | } |
||
4652 | |||
4653 | $f_schema = $this->_schema; |
||
4654 | $this->fieldClean($f_schema); |
||
4655 | $this->fieldClean($table); |
||
4656 | $this->fieldArrayClean($fields); |
||
4657 | $this->fieldClean($name); |
||
4658 | $this->fieldClean($tablespace); |
||
4659 | |||
4660 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD "; |
||
4661 | if ($name != '') { |
||
4662 | $sql .= "CONSTRAINT \"{$name}\" "; |
||
4663 | } |
||
4664 | |||
4665 | $sql .= 'UNIQUE ("' . join('","', $fields) . '")'; |
||
4666 | |||
4667 | if ($tablespace != '' && $this->hasTablespaces()) { |
||
4668 | $sql .= " USING INDEX TABLESPACE \"{$tablespace}\""; |
||
4669 | } |
||
4670 | |||
4671 | return $this->execute($sql); |
||
4672 | } |
||
4673 | |||
4674 | // Function functions |
||
4675 | |||
4676 | /** |
||
4677 | * Adds a check constraint to a table |
||
4678 | * |
||
4679 | * @param $table The table to which to add the check |
||
4680 | * @param $definition The definition of the check |
||
4681 | * @param string $name (optional) The name to give the check, otherwise default name is assigned |
||
4682 | * @return \PHPPgAdmin\Database\A 0 success |
||
4683 | */ |
||
4684 | View Code Duplication | public function addCheckConstraint($table, $definition, $name = '') |
|
4685 | { |
||
4686 | $f_schema = $this->_schema; |
||
4687 | $this->fieldClean($f_schema); |
||
4688 | $this->fieldClean($table); |
||
4689 | $this->fieldClean($name); |
||
4690 | // @@ How the heck do you clean a definition??? |
||
4691 | |||
4692 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD "; |
||
4693 | if ($name != '') { |
||
4694 | $sql .= "CONSTRAINT \"{$name}\" "; |
||
4695 | } |
||
4696 | |||
4697 | $sql .= "CHECK ({$definition})"; |
||
4698 | |||
4699 | return $this->execute($sql); |
||
4700 | } |
||
4701 | |||
4702 | /** |
||
4703 | * Drops a check constraint from a table |
||
4704 | * |
||
4705 | * @param $table The table from which to drop the check |
||
4706 | * @param $name The name of the check to be dropped |
||
4707 | * @return bool|int 0 success |
||
4708 | */ |
||
4709 | public function dropCheckConstraint($table, $name) |
||
4710 | { |
||
4711 | $f_schema = $this->_schema; |
||
4712 | $this->fieldClean($f_schema); |
||
4713 | $c_schema = $this->_schema; |
||
4714 | $this->clean($c_schema); |
||
4715 | $c_table = $table; |
||
4716 | $this->fieldClean($table); |
||
4717 | $this->clean($c_table); |
||
4718 | $this->clean($name); |
||
4719 | |||
4720 | // Begin transaction |
||
4721 | $status = $this->beginTransaction(); |
||
4722 | if ($status != 0) { |
||
4723 | return -2; |
||
4724 | } |
||
4725 | |||
4726 | // Properly lock the table |
||
4727 | $sql = "LOCK TABLE \"{$f_schema}\".\"{$table}\" IN ACCESS EXCLUSIVE MODE"; |
||
4728 | $status = $this->execute($sql); |
||
4729 | if ($status != 0) { |
||
4730 | $this->rollbackTransaction(); |
||
4731 | |||
4732 | return -3; |
||
4733 | } |
||
4734 | |||
4735 | // Delete the check constraint |
||
4736 | $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}' |
||
4737 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE |
||
4738 | nspname = '{$c_schema}')) AND rcname='{$name}'"; |
||
4739 | $status = $this->execute($sql); |
||
4740 | if ($status != 0) { |
||
4741 | $this->rollbackTransaction(); |
||
4742 | |||
4743 | return -4; |
||
4744 | } |
||
4745 | |||
4746 | // Update the pg_class catalog to reflect the new number of checks |
||
4747 | $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE |
||
4748 | rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}' |
||
4749 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE |
||
4750 | nspname = '{$c_schema}'))) |
||
4751 | WHERE relname='{$c_table}'"; |
||
4752 | $status = $this->execute($sql); |
||
4753 | if ($status != 0) { |
||
4754 | $this->rollbackTransaction(); |
||
4755 | |||
4756 | return -4; |
||
4757 | } |
||
4758 | |||
4759 | // Otherwise, close the transaction |
||
4760 | return $this->endTransaction(); |
||
4761 | } |
||
4762 | |||
4763 | /** |
||
4764 | * Adds a foreign key constraint to a table |
||
4765 | * |
||
4766 | * @param $table |
||
4767 | * @param $targschema The schema that houses the target table to which to add the foreign key |
||
4768 | * @param $targtable The table to which to add the foreign key |
||
4769 | * @param $sfields (array) An array of source fields over which to add the foreign key |
||
4770 | * @param $tfields (array) An array of target fields over which to add the foreign key |
||
4771 | * @param $upd_action The action for updates (eg. RESTRICT) |
||
4772 | * @param $del_action The action for deletes (eg. RESTRICT) |
||
4773 | * @param $match The match type (eg. MATCH FULL) |
||
4774 | * @param $deferrable The deferrability (eg. NOT DEFERRABLE) |
||
4775 | * @param $initially |
||
4776 | * @param string $name (optional) The name to give the key, otherwise default name is assigned |
||
4777 | * @return \PHPPgAdmin\Database\A 0 success |
||
4778 | * @internal param \PHPPgAdmin\Database\The $target table that contains the target columns |
||
4779 | * @internal param \PHPPgAdmin\Database\The $intially initial deferrability (eg. INITIALLY IMMEDIATE) |
||
4780 | */ |
||
4781 | public function addForeignKey( |
||
4782 | $table, |
||
4783 | $targschema, |
||
4784 | $targtable, |
||
4785 | $sfields, |
||
4786 | $tfields, |
||
4787 | $upd_action, |
||
4788 | $del_action, |
||
4789 | $match, |
||
4790 | $deferrable, |
||
4791 | $initially, |
||
4792 | $name = '' |
||
4793 | ) { |
||
4794 | if (!is_array($sfields) || sizeof($sfields) == 0 || |
||
4795 | !is_array($tfields) || sizeof($tfields) == 0) { |
||
4796 | return -1; |
||
4797 | } |
||
4798 | |||
4799 | $f_schema = $this->_schema; |
||
4800 | $this->fieldClean($f_schema); |
||
4801 | $this->fieldClean($table); |
||
4802 | $this->fieldClean($targschema); |
||
4803 | $this->fieldClean($targtable); |
||
4804 | $this->fieldArrayClean($sfields); |
||
4805 | $this->fieldArrayClean($tfields); |
||
4806 | $this->fieldClean($name); |
||
4807 | |||
4808 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD "; |
||
4809 | if ($name != '') { |
||
4810 | $sql .= "CONSTRAINT \"{$name}\" "; |
||
4811 | } |
||
4812 | |||
4813 | $sql .= 'FOREIGN KEY ("' . join('","', $sfields) . '") '; |
||
4814 | // Target table needs to be fully qualified |
||
4815 | $sql .= "REFERENCES \"{$targschema}\".\"{$targtable}\"(\"" . join('","', $tfields) . '") '; |
||
4816 | if ($match != $this->fkmatches[0]) { |
||
4817 | $sql .= " {$match}"; |
||
4818 | } |
||
4819 | |||
4820 | if ($upd_action != $this->fkactions[0]) { |
||
4821 | $sql .= " ON UPDATE {$upd_action}"; |
||
4822 | } |
||
4823 | |||
4824 | if ($del_action != $this->fkactions[0]) { |
||
4825 | $sql .= " ON DELETE {$del_action}"; |
||
4826 | } |
||
4827 | |||
4828 | if ($deferrable != $this->fkdeferrable[0]) { |
||
4829 | $sql .= " {$deferrable}"; |
||
4830 | } |
||
4831 | |||
4832 | if ($initially != $this->fkinitial[0]) { |
||
4833 | $sql .= " {$initially}"; |
||
4834 | } |
||
4835 | |||
4836 | return $this->execute($sql); |
||
4837 | } |
||
4838 | |||
4839 | /** |
||
4840 | * Removes a constraint from a relation |
||
4841 | * |
||
4842 | * @param $constraint The constraint to drop |
||
4843 | * @param $relation The relation from which to drop |
||
4844 | * @param $type The type of constraint (c, f, u or p) |
||
4845 | * @param $cascade True to cascade drop, false to restrict |
||
4846 | * @return \PHPPgAdmin\Database\A 0 success |
||
4847 | */ |
||
4848 | View Code Duplication | public function dropConstraint($constraint, $relation, $type, $cascade) |
|
4849 | { |
||
4850 | $f_schema = $this->_schema; |
||
4851 | $this->fieldClean($f_schema); |
||
4852 | $this->fieldClean($constraint); |
||
4853 | $this->fieldClean($relation); |
||
4854 | |||
4855 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\""; |
||
4856 | if ($cascade) { |
||
4857 | $sql .= ' CASCADE'; |
||
4858 | } |
||
4859 | |||
4860 | return $this->execute($sql); |
||
4861 | } |
||
4862 | |||
4863 | /** |
||
4864 | * A function for getting all columns linked by foreign keys given a group of tables |
||
4865 | * |
||
4866 | * @param $tables multi dimensional assoc array that holds schema and table name |
||
4867 | * @return A recordset of linked tables and columns |
||
4868 | * @return -1 $tables isn't an array |
||
4869 | */ |
||
4870 | public function getLinkingKeys($tables) |
||
4871 | { |
||
4872 | if (!is_array($tables)) { |
||
4873 | return -1; |
||
4874 | } |
||
4875 | |||
4876 | $this->clean($tables[0]['tablename']); |
||
4877 | $this->clean($tables[0]['schemaname']); |
||
4878 | $tables_list = "'{$tables[0]['tablename']}'"; |
||
4879 | $schema_list = "'{$tables[0]['schemaname']}'"; |
||
4880 | $schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'"; |
||
4881 | |||
4882 | for ($i = 1; $i < sizeof($tables); $i++) { |
||
4883 | $this->clean($tables[$i]['tablename']); |
||
4884 | $this->clean($tables[$i]['schemaname']); |
||
4885 | $tables_list .= ", '{$tables[$i]['tablename']}'"; |
||
4886 | $schema_list .= ", '{$tables[$i]['schemaname']}'"; |
||
4887 | $schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'"; |
||
4888 | } |
||
4889 | |||
4890 | $maxDimension = 1; |
||
4891 | |||
4892 | $sql = " |
||
4893 | SELECT DISTINCT |
||
4894 | array_dims(pc.conkey) AS arr_dim, |
||
4895 | pgc1.relname AS p_table |
||
4896 | FROM |
||
4897 | pg_catalog.pg_constraint AS pc, |
||
4898 | pg_catalog.pg_class AS pgc1 |
||
4899 | WHERE |
||
4900 | pc.contype = 'f' |
||
4901 | AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode) |
||
4902 | AND pgc1.relname IN ($tables_list) |
||
4903 | "; |
||
4904 | |||
4905 | //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array |
||
4906 | $rs = $this->selectSet($sql); |
||
4907 | while (!$rs->EOF) { |
||
4908 | $arrData = explode(':', $rs->fields['arr_dim']); |
||
4909 | $tmpDimension = intval(substr($arrData[1], 0, strlen($arrData[1] - 1))); |
||
4910 | $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension; |
||
4911 | $rs->MoveNext(); |
||
4912 | } |
||
4913 | |||
4914 | //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query |
||
4915 | $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) '; |
||
4916 | for ($i = 2; $i <= $maxDimension; $i++) { |
||
4917 | $cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) "; |
||
4918 | } |
||
4919 | $cons_str .= ') '; |
||
4920 | |||
4921 | $sql = " |
||
4922 | SELECT |
||
4923 | pgc1.relname AS p_table, |
||
4924 | pgc2.relname AS f_table, |
||
4925 | pfield.attname AS p_field, |
||
4926 | cfield.attname AS f_field, |
||
4927 | pgns1.nspname AS p_schema, |
||
4928 | pgns2.nspname AS f_schema |
||
4929 | FROM |
||
4930 | pg_catalog.pg_constraint AS pc, |
||
4931 | pg_catalog.pg_class AS pgc1, |
||
4932 | pg_catalog.pg_class AS pgc2, |
||
4933 | pg_catalog.pg_attribute AS pfield, |
||
4934 | pg_catalog.pg_attribute AS cfield, |
||
4935 | (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns1, |
||
4936 | (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns2 |
||
4937 | WHERE |
||
4938 | pc.contype = 'f' |
||
4939 | AND pgc1.relnamespace = pgns1.ns_id |
||
4940 | AND pgc2.relnamespace = pgns2.ns_id |
||
4941 | AND pc.conrelid = pgc1.relfilenode |
||
4942 | AND pc.confrelid = pgc2.relfilenode |
||
4943 | AND pfield.attrelid = pc.conrelid |
||
4944 | AND cfield.attrelid = pc.confrelid |
||
4945 | AND $cons_str |
||
4946 | AND pgns1.nspname || '.' || pgc1.relname IN ($schema_tables_list) |
||
4947 | AND pgns2.nspname || '.' || pgc2.relname IN ($schema_tables_list) |
||
4948 | "; |
||
4949 | |||
4950 | return $this->selectSet($sql); |
||
4951 | } |
||
4952 | |||
4953 | /** |
||
4954 | * Finds the foreign keys that refer to the specified table |
||
4955 | * |
||
4956 | * @param $table The table to find referrers for |
||
4957 | * @return A recordset |
||
4958 | */ |
||
4959 | View Code Duplication | public function getReferrers($table) |
|
4960 | { |
||
4961 | $this->clean($table); |
||
4962 | |||
4963 | $status = $this->beginTransaction(); |
||
4964 | if ($status != 0) { |
||
4965 | return -1; |
||
4966 | } |
||
4967 | |||
4968 | $c_schema = $this->_schema; |
||
4969 | $this->clean($c_schema); |
||
4970 | |||
4971 | $sql = " |
||
4972 | SELECT |
||
4973 | pn.nspname, |
||
4974 | pl.relname, |
||
4975 | pc.conname, |
||
4976 | pg_catalog.pg_get_constraintdef(pc.oid) AS consrc |
||
4977 | FROM |
||
4978 | pg_catalog.pg_constraint pc, |
||
4979 | pg_catalog.pg_namespace pn, |
||
4980 | pg_catalog.pg_class pl |
||
4981 | WHERE |
||
4982 | pc.connamespace = pn.oid |
||
4983 | AND pc.conrelid = pl.oid |
||
4984 | AND pc.contype = 'f' |
||
4985 | AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' |
||
4986 | AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||
4987 | WHERE nspname='{$c_schema}')) |
||
4988 | ORDER BY 1,2,3 |
||
4989 | "; |
||
4990 | |||
4991 | return $this->selectSet($sql); |
||
4992 | } |
||
4993 | |||
4994 | // Type functions |
||
4995 | |||
4996 | /** |
||
4997 | * Gets all information for a single domain |
||
4998 | * |
||
4999 | * @param $domain The name of the domain to fetch |
||
5000 | * @return A recordset |
||
5001 | */ |
||
5002 | public function getDomain($domain) |
||
5003 | { |
||
5004 | $c_schema = $this->_schema; |
||
5005 | $this->clean($c_schema); |
||
5006 | $this->clean($domain); |
||
5007 | |||
5008 | $sql = " |
||
5009 | SELECT |
||
5010 | t.typname AS domname, |
||
5011 | pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype, |
||
5012 | t.typnotnull AS domnotnull, |
||
5013 | t.typdefault AS domdef, |
||
5014 | pg_catalog.pg_get_userbyid(t.typowner) AS domowner, |
||
5015 | pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment |
||
5016 | FROM |
||
5017 | pg_catalog.pg_type t |
||
5018 | WHERE |
||
5019 | t.typtype = 'd' |
||
5020 | AND t.typname = '{$domain}' |
||
5021 | AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||
5022 | WHERE nspname = '{$c_schema}')"; |
||
5023 | |||
5024 | return $this->selectSet($sql); |
||
5025 | } |
||
5026 | |||
5027 | /** |
||
5028 | * Return all domains in current schema. Excludes domain constraints. |
||
5029 | * |
||
5030 | * @return All tables, sorted alphabetically |
||
5031 | */ |
||
5032 | public function getDomains() |
||
5033 | { |
||
5034 | $c_schema = $this->_schema; |
||
5035 | $this->clean($c_schema); |
||
5036 | |||
5037 | $sql = " |
||
5038 | SELECT |
||
5039 | t.typname AS domname, |
||
5040 | pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype, |
||
5041 | t.typnotnull AS domnotnull, |
||
5042 | t.typdefault AS domdef, |
||
5043 | pg_catalog.pg_get_userbyid(t.typowner) AS domowner, |
||
5044 | pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment |
||
5045 | FROM |
||
5046 | pg_catalog.pg_type t |
||
5047 | WHERE |
||
5048 | t.typtype = 'd' |
||
5049 | AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace |
||
5050 | WHERE nspname='{$c_schema}') |
||
5051 | ORDER BY t.typname"; |
||
5052 | |||
5053 | return $this->selectSet($sql); |
||
5054 | } |
||
5055 | |||
5056 | /** |
||
5057 | * Get domain constraints |
||
5058 | * |
||
5059 | * @param $domain The name of the domain whose constraints to fetch |
||
5060 | * @return A recordset |
||
5061 | */ |
||
5062 | public function getDomainConstraints($domain) |
||
5063 | { |
||
5064 | $c_schema = $this->_schema; |
||
5065 | $this->clean($c_schema); |
||
5066 | $this->clean($domain); |
||
5067 | |||
5068 | $sql = " |
||
5069 | SELECT |
||
5070 | conname, |
||
5071 | contype, |
||
5072 | pg_catalog.pg_get_constraintdef(oid, true) AS consrc |
||
5073 | FROM |
||
5074 | pg_catalog.pg_constraint |
||
5075 | WHERE |
||
5076 | contypid = ( |
||
5077 | SELECT oid FROM pg_catalog.pg_type |
||
5078 | WHERE typname='{$domain}' |
||
5079 | AND typnamespace = ( |
||
5080 | SELECT oid FROM pg_catalog.pg_namespace |
||
5081 | WHERE nspname = '{$c_schema}') |
||
5082 | ) |
||
5083 | ORDER BY conname"; |
||
5084 | |||
5085 | return $this->selectSet($sql); |
||
5086 | } |
||
5087 | |||
5088 | /** |
||
5089 | * Creates a domain |
||
5090 | * |
||
5091 | * @param $domain The name of the domain to create |
||
5092 | * @param $type The base type for the domain |
||
5093 | * @param $length Optional type length |
||
5094 | * @param $array True for array type, false otherwise |
||
5095 | * @param $notnull True for NOT NULL, false otherwise |
||
5096 | * @param $default Default value for domain |
||
5097 | * @param $check A CHECK constraint if there is one |
||
5098 | * @return \PHPPgAdmin\Database\A 0 success |
||
5099 | */ |
||
5100 | public function createDomain($domain, $type, $length, $array, $notnull, $default, $check) |
||
5101 | { |
||
5102 | $f_schema = $this->_schema; |
||
5103 | $this->fieldClean($f_schema); |
||
5104 | $this->fieldClean($domain); |
||
5105 | |||
5106 | $sql = "CREATE DOMAIN \"{$f_schema}\".\"{$domain}\" AS "; |
||
5107 | |||
5108 | if ($length == '') { |
||
5109 | $sql .= $type; |
||
5110 | } else { |
||
5111 | switch ($type) { |
||
5112 | // Have to account for weird placing of length for with/without |
||
5113 | // time zone types |
||
5114 | case 'timestamp with time zone': |
||
5115 | case 'timestamp without time zone': |
||
5116 | $qual = substr($type, 9); |
||
5117 | $sql .= "timestamp({$length}){$qual}"; |
||
5118 | break; |
||
5119 | case 'time with time zone': |
||
5120 | case 'time without time zone': |
||
5121 | $qual = substr($type, 4); |
||
5122 | $sql .= "time({$length}){$qual}"; |
||
5123 | break; |
||
5124 | default: |
||
5125 | $sql .= "{$type}({$length})"; |
||
5126 | } |
||
5127 | } |
||
5128 | |||
5129 | // Add array qualifier, if requested |
||
5130 | if ($array) { |
||
5131 | $sql .= '[]'; |
||
5132 | } |
||
5133 | |||
5134 | if ($notnull) { |
||
5135 | $sql .= ' NOT NULL'; |
||
5136 | } |
||
5137 | |||
5138 | if ($default != '') { |
||
5139 | $sql .= " DEFAULT {$default}"; |
||
5140 | } |
||
5141 | |||
5142 | if ($this->hasDomainConstraints() && $check != '') { |
||
5143 | $sql .= " CHECK ({$check})"; |
||
5144 | } |
||
5145 | |||
5146 | return $this->execute($sql); |
||
5147 | } |
||
5148 | |||
5149 | public function hasDomainConstraints() |
||
5150 | { |
||
5151 | return true; |
||
5152 | } |
||
5153 | |||
5154 | /** |
||
5155 | * Alters a domain |
||
5156 | * |
||
5157 | * @param $domain The domain to alter |
||
5158 | * @param $domdefault The domain default |
||
5159 | * @param $domnotnull True for NOT NULL, false otherwise |
||
5160 | * @param $domowner The domain owner |
||
5161 | * @return bool|int 0 success |
||
5162 | */ |
||
5163 | public function alterDomain($domain, $domdefault, $domnotnull, $domowner) |
||
5164 | { |
||
5165 | $f_schema = $this->_schema; |
||
5166 | $this->fieldClean($f_schema); |
||
5167 | $this->fieldClean($domain); |
||
5168 | $this->fieldClean($domowner); |
||
5169 | |||
5170 | $status = $this->beginTransaction(); |
||
5171 | if ($status != 0) { |
||
5172 | $this->rollbackTransaction(); |
||
5173 | |||
5174 | return -1; |
||
5175 | } |
||
5176 | |||
5177 | // Default |
||
5178 | if ($domdefault == '') { |
||
5179 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP DEFAULT"; |
||
5180 | } else { |
||
5181 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET DEFAULT {$domdefault}"; |
||
5182 | } |
||
5183 | |||
5184 | $status = $this->execute($sql); |
||
5185 | if ($status != 0) { |
||
5186 | $this->rollbackTransaction(); |
||
5187 | |||
5188 | return -2; |
||
5189 | } |
||
5190 | |||
5191 | // NOT NULL |
||
5192 | if ($domnotnull) { |
||
5193 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET NOT NULL"; |
||
5194 | } else { |
||
5195 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP NOT NULL"; |
||
5196 | } |
||
5197 | |||
5198 | $status = $this->execute($sql); |
||
5199 | if ($status != 0) { |
||
5200 | $this->rollbackTransaction(); |
||
5201 | |||
5202 | return -3; |
||
5203 | } |
||
5204 | |||
5205 | // Owner |
||
5206 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" OWNER TO \"{$domowner}\""; |
||
5207 | |||
5208 | $status = $this->execute($sql); |
||
5209 | if ($status != 0) { |
||
5210 | $this->rollbackTransaction(); |
||
5211 | |||
5212 | return -4; |
||
5213 | } |
||
5214 | |||
5215 | return $this->endTransaction(); |
||
5216 | } |
||
5217 | |||
5218 | /** |
||
5219 | * Drops a domain. |
||
5220 | * |
||
5221 | * @param $domain The name of the domain to drop |
||
5222 | * @param $cascade True to cascade drop, false to restrict |
||
5223 | * @return \PHPPgAdmin\Database\A 0 success |
||
5224 | */ |
||
5225 | View Code Duplication | public function dropDomain($domain, $cascade) |
|
5226 | { |
||
5227 | $f_schema = $this->_schema; |
||
5228 | $this->fieldClean($f_schema); |
||
5229 | $this->fieldClean($domain); |
||
5230 | |||
5231 | $sql = "DROP DOMAIN \"{$f_schema}\".\"{$domain}\""; |
||
5232 | if ($cascade) { |
||
5233 | $sql .= ' CASCADE'; |
||
5234 | } |
||
5235 | |||
5236 | return $this->execute($sql); |
||
5237 | } |
||
5238 | |||
5239 | /** |
||
5240 | * Adds a check constraint to a domain |
||
5241 | * |
||
5242 | * @param $domain The domain to which to add the check |
||
5243 | * @param $definition The definition of the check |
||
5244 | * @param string $name (optional) The name to give the check, otherwise default name is assigned |
||
5245 | * @return \PHPPgAdmin\Database\A 0 success |
||
5246 | */ |
||
5247 | View Code Duplication | public function addDomainCheckConstraint($domain, $definition, $name = '') |
|
5248 | { |
||
5249 | $f_schema = $this->_schema; |
||
5250 | $this->fieldClean($f_schema); |
||
5251 | $this->fieldClean($domain); |
||
5252 | $this->fieldClean($name); |
||
5253 | |||
5254 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" ADD "; |
||
5255 | if ($name != '') { |
||
5256 | $sql .= "CONSTRAINT \"{$name}\" "; |
||
5257 | } |
||
5258 | |||
5259 | $sql .= "CHECK ({$definition})"; |
||
5260 | |||
5261 | return $this->execute($sql); |
||
5262 | } |
||
5263 | |||
5264 | /** |
||
5265 | * Drops a domain constraint |
||
5266 | * |
||
5267 | * @param $domain The domain from which to remove the constraint |
||
5268 | * @param $constraint The constraint to remove |
||
5269 | * @param $cascade True to cascade, false otherwise |
||
5270 | * @return \PHPPgAdmin\Database\A 0 success |
||
5271 | */ |
||
5272 | View Code Duplication | public function dropDomainConstraint($domain, $constraint, $cascade) |
|
5273 | { |
||
5274 | $f_schema = $this->_schema; |
||
5275 | $this->fieldClean($f_schema); |
||
5276 | $this->fieldClean($domain); |
||
5277 | $this->fieldClean($constraint); |
||
5278 | |||
5279 | $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP CONSTRAINT \"{$constraint}\""; |
||
5280 | if ($cascade) { |
||
5281 | $sql .= ' CASCADE'; |
||
5282 | } |
||
5283 | |||
5284 | return $this->execute($sql); |
||
5285 | } |
||
5286 | |||
5287 | // Rule functions |
||
5288 | |||
5289 | /** |
||
5290 | * Returns an array containing a function's properties |
||
5291 | * |
||
5292 | * @param $f The array of data for the function |
||
5293 | * @return An array containing the properties |
||
5294 | */ |
||
5295 | public function getFunctionProperties($f) |
||
5296 | { |
||
5297 | $temp = []; |
||
5298 | |||
5299 | // Volatility |
||
5300 | if ($f['provolatile'] == 'v') { |
||
5301 | $temp[] = 'VOLATILE'; |
||
5302 | } elseif ($f['provolatile'] == 'i') { |
||
5303 | $temp[] = 'IMMUTABLE'; |
||
5304 | } elseif ($f['provolatile'] == 's') { |
||
5305 | $temp[] = 'STABLE'; |
||
5306 | } else { |
||
5307 | return -1; |
||
5308 | } |
||
5309 | |||
5310 | // Null handling |
||
5311 | $f['proisstrict'] = $this->phpBool($f['proisstrict']); |
||
5312 | if ($f['proisstrict']) { |
||
5313 | $temp[] = 'RETURNS NULL ON NULL INPUT'; |
||
5314 | } else { |
||
5315 | $temp[] = 'CALLED ON NULL INPUT'; |
||
5316 | } |
||
5317 | |||
5318 | // Security |
||
5319 | $f['prosecdef'] = $this->phpBool($f['prosecdef']); |
||
5320 | if ($f['prosecdef']) { |
||
5321 | $temp[] = 'SECURITY DEFINER'; |
||
5322 | } else { |
||
5323 | $temp[] = 'SECURITY INVOKER'; |
||
5324 | } |
||
5325 | |||
5326 | return $temp; |
||
5327 | } |
||
5328 | |||
5329 | /** |
||
5330 | * Updates (replaces) a function. |
||
5331 | * |
||
5332 | * @param $function_oid The OID of the function |
||
5333 | * @param $funcname The name of the function to create |
||
5334 | * @param $newname The new name for the function |
||
5335 | * @param $args The array of argument types |
||
5336 | * @param $returns The return type |
||
5337 | * @param $definition The definition for the new function |
||
5338 | * @param $language The language the function is written for |
||
5339 | * @param $flags An array of optional flags |
||
5340 | * @param $setof True if returns a set, false otherwise |
||
5341 | * @param $funcown |
||
5342 | * @param $newown |
||
5343 | * @param $funcschema |
||
5344 | * @param $newschema |
||
5345 | * @param $cost |
||
5346 | * @param $rows |
||
5347 | * @param $comment The comment on the function |
||
5348 | * @return bool|int 0 success |
||
5349 | */ |
||
5350 | public function setFunction( |
||
5351 | $function_oid, |
||
5352 | $funcname, |
||
5353 | $newname, |
||
5354 | $args, |
||
5355 | $returns, |
||
5356 | $definition, |
||
5357 | $language, |
||
5358 | $flags, |
||
5359 | $setof, |
||
5360 | $funcown, |
||
5361 | $newown, |
||
5362 | $funcschema, |
||
5363 | $newschema, |
||
5364 | $cost, |
||
5365 | $rows, |
||
5366 | $comment |
||
5367 | ) { |
||
5368 | // Begin a transaction |
||
5369 | $status = $this->beginTransaction(); |
||
5370 | if ($status != 0) { |
||
5371 | $this->rollbackTransaction(); |
||
5372 | |||
5373 | return -1; |
||
5374 | } |
||
5375 | |||
5376 | // Replace the existing function |
||
5377 | $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true); |
||
5378 | if ($status != 0) { |
||
5379 | $this->rollbackTransaction(); |
||
5380 | |||
5381 | return $status; |
||
5382 | } |
||
5383 | |||
5384 | $f_schema = $this->_schema; |
||
5385 | $this->fieldClean($f_schema); |
||
5386 | |||
5387 | // Rename the function, if necessary |
||
5388 | $this->fieldClean($newname); |
||
5389 | /* $funcname is escaped in createFunction */ |
||
5390 | if ($funcname != $newname) { |
||
5391 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\""; |
||
5392 | $status = $this->execute($sql); |
||
5393 | if ($status != 0) { |
||
5394 | $this->rollbackTransaction(); |
||
5395 | |||
5396 | return -5; |
||
5397 | } |
||
5398 | |||
5399 | $funcname = $newname; |
||
5400 | } |
||
5401 | |||
5402 | // Alter the owner, if necessary |
||
5403 | if ($this->hasFunctionAlterOwner()) { |
||
5404 | $this->fieldClean($newown); |
||
5405 | if ($funcown != $newown) { |
||
5406 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\""; |
||
5407 | $status = $this->execute($sql); |
||
5408 | if ($status != 0) { |
||
5409 | $this->rollbackTransaction(); |
||
5410 | |||
5411 | return -6; |
||
5412 | } |
||
5413 | } |
||
5414 | } |
||
5415 | |||
5416 | // Alter the schema, if necessary |
||
5417 | if ($this->hasFunctionAlterSchema()) { |
||
5418 | $this->fieldClean($newschema); |
||
5419 | /* $funcschema is escaped in createFunction */ |
||
5420 | if ($funcschema != $newschema) { |
||
5421 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\""; |
||
5422 | $status = $this->execute($sql); |
||
5423 | if ($status != 0) { |
||
5424 | $this->rollbackTransaction(); |
||
5425 | |||
5426 | return -7; |
||
5427 | } |
||
5428 | } |
||
5429 | } |
||
5430 | |||
5431 | return $this->endTransaction(); |
||
5432 | } |
||
5433 | |||
5434 | /** |
||
5435 | * Creates a new function. |
||
5436 | * |
||
5437 | * @param $funcname The name of the function to create |
||
5438 | * @param $args A comma separated string of types |
||
5439 | * @param $returns The return type |
||
5440 | * @param $definition The definition for the new function |
||
5441 | * @param $language The language the function is written for |
||
5442 | * @param $flags An array of optional flags |
||
5443 | * @param $setof True if it returns a set, false otherwise |
||
5444 | * @param $cost cost the planner should use in the function execution step |
||
5445 | * @param $rows number of rows planner should estimate will be returned |
||
5446 | * @param $comment Comment for the function |
||
5447 | * @param bool $replace (optional) True if OR REPLACE, false for normal |
||
5448 | * @return bool|int 0 success |
||
5449 | */ |
||
5450 | public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false) |
||
5451 | { |
||
5452 | |||
5453 | // Begin a transaction |
||
5454 | $status = $this->beginTransaction(); |
||
5455 | if ($status != 0) { |
||
5456 | $this->rollbackTransaction(); |
||
5457 | |||
5458 | return -1; |
||
5459 | } |
||
5460 | |||
5461 | $this->fieldClean($funcname); |
||
5462 | $this->clean($args); |
||
5463 | $this->fieldClean($language); |
||
5464 | $this->arrayClean($flags); |
||
5465 | $this->clean($cost); |
||
5466 | $this->clean($rows); |
||
5467 | $f_schema = $this->_schema; |
||
5468 | $this->fieldClean($f_schema); |
||
5469 | |||
5470 | $sql = 'CREATE'; |
||
5471 | if ($replace) { |
||
5472 | $sql .= ' OR REPLACE'; |
||
5473 | } |
||
5474 | |||
5475 | $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" ("; |
||
5476 | |||
5477 | if ($args != '') { |
||
5478 | $sql .= $args; |
||
5479 | } |
||
5480 | |||
5481 | // For some reason, the returns field cannot have quotes... |
||
5482 | $sql .= ') RETURNS '; |
||
5483 | if ($setof) { |
||
5484 | $sql .= 'SETOF '; |
||
5485 | } |
||
5486 | |||
5487 | $sql .= "{$returns} AS "; |
||
5488 | |||
5489 | View Code Duplication | if (is_array($definition)) { |
|
5490 | $this->arrayClean($definition); |
||
5491 | $sql .= "'" . $definition[0] . "'"; |
||
5492 | if ($definition[1]) { |
||
5493 | $sql .= ",'" . $definition[1] . "'"; |
||
5494 | } |
||
5495 | } else { |
||
5496 | $this->clean($definition); |
||
5497 | $sql .= "'" . $definition . "'"; |
||
5498 | } |
||
5499 | |||
5500 | $sql .= " LANGUAGE \"{$language}\""; |
||
5501 | |||
5502 | // Add costs |
||
5503 | if (!empty($cost)) { |
||
5504 | $sql .= " COST {$cost}"; |
||
5505 | } |
||
5506 | |||
5507 | if ($rows != 0) { |
||
5508 | $sql .= " ROWS {$rows}"; |
||
5509 | } |
||
5510 | |||
5511 | // Add flags |
||
5512 | foreach ($flags as $v) { |
||
5513 | // Skip default flags |
||
5514 | if ($v == '') { |
||
5515 | continue; |
||
5516 | } |
||
5517 | |||
5518 | $sql .= "\n{$v}"; |
||
5519 | } |
||
5520 | |||
5521 | $status = $this->execute($sql); |
||
5522 | if ($status != 0) { |
||
5523 | $this->rollbackTransaction(); |
||
5524 | |||
5525 | return -3; |
||
5526 | } |
||
5527 | |||
5528 | /* set the comment */ |
||
5529 | $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment); |
||
5530 | if ($status != 0) { |
||
5531 | $this->rollbackTransaction(); |
||
5532 | |||
5533 | return -4; |
||
5534 | } |
||
5535 | |||
5536 | return $this->endTransaction(); |
||
5537 | } |
||
5538 | |||
5539 | public function hasFunctionAlterOwner() |
||
5540 | { |
||
5541 | return true; |
||
5542 | } |
||
5543 | |||
5544 | // Trigger functions |
||
5545 | |||
5546 | public function hasFunctionAlterSchema() |
||
5547 | { |
||
5548 | return true; |
||
5549 | } |
||
5550 | |||
5551 | /** |
||
5552 | * Drops a function. |
||
5553 | * |
||
5554 | * @param $function_oid The OID of the function to drop |
||
5555 | * @param $cascade True to cascade drop, false to restrict |
||
5556 | * @return \PHPPgAdmin\Database\A 0 success |
||
5557 | */ |
||
5558 | public function dropFunction($function_oid, $cascade) |
||
5572 | } |
||
5573 | |||
5574 | /** |
||
5575 | * Returns all details for a particular function |
||
5576 | * |
||
5577 | * @param $function_oid |
||
5578 | * @return \PHPPgAdmin\Database\Function info |
||
5579 | * @internal param \PHPPgAdmin\Database\The $func name of the function to retrieve |
||
5580 | */ |
||
5581 | public function getFunction($function_oid) |
||
5582 | { |
||
5583 | $this->clean($function_oid); |
||
5584 | |||
5585 | $sql = " |
||
5586 | SELECT |
||
5587 | pc.oid AS prooid, proname, |
||
5588 | pg_catalog.pg_get_userbyid(proowner) AS proowner, |
||
5589 | nspname as proschema, lanname as prolanguage, procost, prorows, |
||
5590 | pg_catalog.format_type(prorettype, NULL) as proresult, prosrc, |
||
5591 | probin, proretset, proisstrict, provolatile, prosecdef, |
||
5592 | pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments, |
||
5593 | proargnames AS proargnames, |
||
5594 | pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment, |
||
5595 | proconfig, |
||
5596 | (select array_agg( (select typname from pg_type pt |
||
5597 | where pt.oid = p.oid) ) from unnest(proallargtypes) p) |
||
5598 | AS proallarguments, |
||
5599 | proargmodes |
||
5600 | FROM |
||
5601 | pg_catalog.pg_proc pc, pg_catalog.pg_language pl, |
||
5602 | pg_catalog.pg_namespace pn |
||
5603 | WHERE |
||
5604 | pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid |
||
5605 | AND pc.pronamespace = pn.oid |
||
5606 | "; |
||
5607 | |||
5608 | return $this->selectSet($sql); |
||
5609 | } |
||
5610 | |||
5611 | /** |
||
5612 | * Returns all details for a particular type |
||
5613 | * |
||
5614 | * @param $typname The name of the view to retrieve |
||
5615 | * @return Type info |
||
5616 | */ |
||
5617 | public function getType($typname) |
||
5618 | { |
||
5619 | $this->clean($typname); |
||
5620 | |||
5621 | $sql = "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign |
||
5622 | FROM pg_type WHERE typname='{$typname}'"; |
||
5623 | |||
5624 | return $this->selectSet($sql); |
||
5625 | } |
||
5626 | |||
5627 | /** |
||
5628 | * Returns a list of all types in the database |
||
5629 | * |
||
5630 | * @param bool|\PHPPgAdmin\Database\If $all If true, will find all available types, if false just those in search path |
||
5631 | * @param bool|\PHPPgAdmin\Database\If $tabletypes If true, will include table types |
||
5632 | * @param bool|\PHPPgAdmin\Database\If $domains If true, will include domains |
||
5633 | * @return \PHPPgAdmin\Database\A recordet |
||
5634 | */ |
||
5635 | public function getTypes($all = false, $tabletypes = false, $domains = false) |
||
5636 | { |
||
5637 | if ($all) { |
||
5638 | $where = '1 = 1'; |
||
5639 | } else { |
||
5640 | $c_schema = $this->_schema; |
||
5641 | $this->clean($c_schema); |
||
5642 | $where = "n.nspname = '{$c_schema}'"; |
||
5643 | } |
||
5644 | // Never show system table types |
||
5645 | $where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg@_%' ESCAPE '@')"; |
||
5646 | |||
5647 | // Create type filter |
||
5648 | $tqry = "'c'"; |
||
5649 | if ($tabletypes) { |
||
5650 | $tqry .= ", 'r', 'v'"; |
||
5651 | } |
||
5652 | |||
5653 | // Create domain filter |
||
5654 | if (!$domains) { |
||
5655 | $where .= " AND t.typtype != 'd'"; |
||
5656 | } |
||
5657 | |||
5658 | $sql = "SELECT |
||
5659 | t.typname AS basename, |
||
5660 | pg_catalog.format_type(t.oid, NULL) AS typname, |
||
5661 | pu.usename AS typowner, |
||
5662 | t.typtype, |
||
5663 | pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment |
||
5664 | FROM (pg_catalog.pg_type t |
||
5665 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace) |
||
5666 | LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid |
||
5667 | WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2})) |
||
5668 | AND t.typname !~ '^_' |
||
5669 | AND {$where} |
||
5670 | ORDER BY typname |
||
5671 | "; |
||
5672 | |||
5673 | return $this->selectSet($sql); |
||
5674 | } |
||
5675 | |||
5676 | /** |
||
5677 | * Creates a new type |
||
5678 | * |
||
5679 | * @param $typname |
||
5680 | * @param $typin |
||
5681 | * @param $typout |
||
5682 | * @param $typlen |
||
5683 | * @param $typdef |
||
5684 | * @param $typelem |
||
5685 | * @param $typdelim |
||
5686 | * @param $typbyval |
||
5687 | * @param $typalign |
||
5688 | * @param $typstorage |
||
5689 | * @return \PHPPgAdmin\Database\A 0 success |
||
5690 | * @internal param $ ... |
||
5691 | */ |
||
5692 | public function createType( |
||
5693 | $typname, |
||
5694 | $typin, |
||
5695 | $typout, |
||
5696 | $typlen, |
||
5697 | $typdef, |
||
5698 | $typelem, |
||
5699 | $typdelim, |
||
5700 | $typbyval, |
||
5701 | $typalign, |
||
5702 | $typstorage |
||
5703 | ) { |
||
5704 | $f_schema = $this->_schema; |
||
5705 | $this->fieldClean($f_schema); |
||
5706 | $this->fieldClean($typname); |
||
5707 | $this->fieldClean($typin); |
||
5708 | $this->fieldClean($typout); |
||
5709 | |||
5710 | $sql = " |
||
5711 | CREATE TYPE \"{$f_schema}\".\"{$typname}\" ( |
||
5712 | INPUT = \"{$typin}\", |
||
5713 | OUTPUT = \"{$typout}\", |
||
5714 | INTERNALLENGTH = {$typlen}"; |
||
5715 | if ($typdef != '') { |
||
5716 | $sql .= ", DEFAULT = {$typdef}"; |
||
5717 | } |
||
5718 | |||
5719 | if ($typelem != '') { |
||
5720 | $sql .= ", ELEMENT = {$typelem}"; |
||
5721 | } |
||
5722 | |||
5723 | if ($typdelim != '') { |
||
5724 | $sql .= ", DELIMITER = {$typdelim}"; |
||
5725 | } |
||
5726 | |||
5727 | if ($typbyval) { |
||
5728 | $sql .= ', PASSEDBYVALUE, '; |
||
5729 | } |
||
5730 | |||
5731 | if ($typalign != '') { |
||
5732 | $sql .= ", ALIGNMENT = {$typalign}"; |
||
5733 | } |
||
5734 | |||
5735 | if ($typstorage != '') { |
||
5736 | $sql .= ", STORAGE = {$typstorage}"; |
||
5737 | } |
||
5738 | |||
5739 | $sql .= ')'; |
||
5740 | |||
5741 | return $this->execute($sql); |
||
5742 | } |
||
5743 | |||
5744 | /** |
||
5745 | * Drops a type. |
||
5746 | * |
||
5747 | * @param $typname The name of the type to drop |
||
5748 | * @param $cascade True to cascade drop, false to restrict |
||
5749 | * @return \PHPPgAdmin\Database\A 0 success |
||
5750 | */ |
||
5751 | View Code Duplication | public function dropType($typname, $cascade) |
|
5752 | { |
||
5753 | $f_schema = $this->_schema; |
||
5754 | $this->fieldClean($f_schema); |
||
5755 | $this->fieldClean($typname); |
||
5756 | |||
5757 | $sql = "DROP TYPE \"{$f_schema}\".\"{$typname}\""; |
||
5758 | if ($cascade) { |
||
5759 | $sql .= ' CASCADE'; |
||
5760 | } |
||
5761 | |||
5762 | return $this->execute($sql); |
||
5763 | } |
||
5764 | |||
5765 | /** |
||
5766 | * Creates a new enum type in the database |
||
5767 | * |
||
5768 | * @param $name The name of the type |
||
5769 | * @param $values An array of values |
||
5770 | * @param $typcomment Type comment |
||
5771 | * @return bool|int 0 success |
||
5772 | */ |
||
5773 | public function createEnumType($name, $values, $typcomment) |
||
5774 | { |
||
5775 | $f_schema = $this->_schema; |
||
5776 | $this->fieldClean($f_schema); |
||
5777 | $this->fieldClean($name); |
||
5778 | |||
5779 | if (empty($values)) { |
||
5780 | return -2; |
||
5781 | } |
||
5782 | |||
5783 | $status = $this->beginTransaction(); |
||
5784 | if ($status != 0) { |
||
5785 | return -1; |
||
5786 | } |
||
5787 | |||
5788 | $values = array_unique($values); |
||
5789 | |||
5790 | $nbval = count($values); |
||
5791 | |||
5792 | for ($i = 0; $i < $nbval; $i++) { |
||
5793 | $this->clean($values[$i]); |
||
5794 | } |
||
5795 | |||
5796 | $sql = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS ENUM ('"; |
||
5797 | $sql .= implode("','", $values); |
||
5798 | $sql .= "')"; |
||
5799 | |||
5800 | $status = $this->execute($sql); |
||
5801 | if ($status) { |
||
5802 | $this->rollbackTransaction(); |
||
5803 | |||
5804 | return -1; |
||
5805 | } |
||
5806 | |||
5807 | View Code Duplication | if ($typcomment != '') { |
|
5808 | $status = $this->setComment('TYPE', $name, '', $typcomment, true); |
||
5809 | if ($status) { |
||
5810 | $this->rollbackTransaction(); |
||
5811 | |||
5812 | return -1; |
||
5813 | } |
||
5814 | } |
||
5815 | |||
5816 | return $this->endTransaction(); |
||
5817 | } |
||
5818 | |||
5819 | /** |
||
5820 | * Get defined values for a given enum |
||
5821 | * |
||
5822 | * @param $name |
||
5823 | * @return \PHPPgAdmin\Database\A recordset |
||
5824 | */ |
||
5825 | public function getEnumValues($name) |
||
5826 | { |
||
5827 | $this->clean($name); |
||
5828 | |||
5829 | $sql = "SELECT enumlabel AS enumval |
||
5830 | FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON (t.oid=e.enumtypid) |
||
5831 | WHERE t.typname = '{$name}' ORDER BY e.oid"; |
||
5832 | |||
5833 | return $this->selectSet($sql); |
||
5834 | } |
||
5835 | |||
5836 | // Operator functions |
||
5837 | |||
5838 | /** |
||
5839 | * Creates a new composite type in the database |
||
5840 | * |
||
5841 | * @param $name The name of the type |
||
5842 | * @param $fields The number of fields |
||
5843 | * @param $field An array of field names |
||
5844 | * @param $type An array of field types |
||
5845 | * @param $array An array of '' or '[]' for each type if it's an array or not |
||
5846 | * @param $length An array of field lengths |
||
5847 | * @param $colcomment An array of comments |
||
5848 | * @param $typcomment Type comment |
||
5849 | * @return bool|int 0 success |
||
5850 | */ |
||
5851 | public function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment) |
||
5852 | { |
||
5853 | $f_schema = $this->_schema; |
||
5854 | $this->fieldClean($f_schema); |
||
5855 | $this->fieldClean($name); |
||
5856 | |||
5857 | $status = $this->beginTransaction(); |
||
5858 | if ($status != 0) { |
||
5859 | return -1; |
||
5860 | } |
||
5861 | |||
5862 | $found = false; |
||
5863 | $first = true; |
||
5864 | $comment_sql = ''; // Accumulate comments for the columns |
||
5865 | $sql = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS ("; |
||
5866 | for ($i = 0; $i < $fields; $i++) { |
||
5867 | $this->fieldClean($field[$i]); |
||
5868 | $this->clean($type[$i]); |
||
5869 | $this->clean($length[$i]); |
||
5870 | $this->clean($colcomment[$i]); |
||
5871 | |||
5872 | // Skip blank columns - for user convenience |
||
5873 | if ($field[$i] == '' || $type[$i] == '') { |
||
5874 | continue; |
||
5875 | } |
||
5876 | |||
5877 | // If not the first column, add a comma |
||
5878 | if (!$first) { |
||
5879 | $sql .= ', '; |
||
5880 | } else { |
||
5881 | $first = false; |
||
5882 | } |
||
5883 | |||
5884 | View Code Duplication | switch ($type[$i]) { |
|
5885 | // Have to account for weird placing of length for with/without |
||
5886 | // time zone types |
||
5887 | case 'timestamp with time zone': |
||
5888 | case 'timestamp without time zone': |
||
5889 | $qual = substr($type[$i], 9); |
||
5890 | $sql .= "\"{$field[$i]}\" timestamp"; |
||
5891 | if ($length[$i] != '') { |
||
5892 | $sql .= "({$length[$i]})"; |
||
5893 | } |
||
5894 | |||
5895 | $sql .= $qual; |
||
5896 | break; |
||
5897 | case 'time with time zone': |
||
5898 | case 'time without time zone': |
||
5899 | $qual = substr($type[$i], 4); |
||
5900 | $sql .= "\"{$field[$i]}\" time"; |
||
5901 | if ($length[$i] != '') { |
||
5902 | $sql .= "({$length[$i]})"; |
||
5903 | } |
||
5904 | |||
5905 | $sql .= $qual; |
||
5906 | break; |
||
5907 | default: |
||
5908 | $sql .= "\"{$field[$i]}\" {$type[$i]}"; |
||
5909 | if ($length[$i] != '') { |
||
5910 | $sql .= "({$length[$i]})"; |
||
5911 | } |
||
5912 | } |
||
5913 | // Add array qualifier if necessary |
||
5914 | if ($array[$i] == '[]') { |
||
5915 | $sql .= '[]'; |
||
5916 | } |
||
5917 | |||
5918 | View Code Duplication | if ($colcomment[$i] != '') { |
|
5919 | $comment_sql .= "COMMENT ON COLUMN \"{$f_schema}\".\"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n"; |
||
5920 | } |
||
5921 | |||
5922 | $found = true; |
||
5923 | } |
||
5924 | |||
5925 | if (!$found) { |
||
5926 | return -1; |
||
5927 | } |
||
5928 | |||
5929 | $sql .= ')'; |
||
5930 | |||
5931 | $status = $this->execute($sql); |
||
5932 | if ($status) { |
||
5933 | $this->rollbackTransaction(); |
||
5934 | |||
5935 | return -1; |
||
5936 | } |
||
5937 | |||
5938 | View Code Duplication | if ($typcomment != '') { |
|
5939 | $status = $this->setComment('TYPE', $name, '', $typcomment, true); |
||
5940 | if ($status) { |
||
5941 | $this->rollbackTransaction(); |
||
5942 | |||
5943 | return -1; |
||
5944 | } |
||
5945 | } |
||
5946 | |||
5947 | View Code Duplication | if ($comment_sql != '') { |
|
5948 | $status = $this->execute($comment_sql); |
||
5949 | if ($status) { |
||
5950 | $this->rollbackTransaction(); |
||
5951 | |||
5952 | return -1; |
||
5953 | } |
||
5954 | } |
||
5955 | |||
5956 | return $this->endTransaction(); |
||
5957 | } |
||
5958 | |||
5959 | /** |
||
5960 | * Returns a list of all casts in the database |
||
5961 | * |
||
5962 | * @return All casts |
||
5963 | */ |
||
5964 | View Code Duplication | public function getCasts() |
|
5965 | { |
||
5966 | $conf = $this->conf; |
||
5967 | |||
5968 | if ($conf['show_system']) { |
||
5969 | $where = ''; |
||
5970 | } else { |
||
5971 | $where = ' |
||
5972 | AND n1.nspname NOT LIKE $$pg\_%$$ |
||
5973 | AND n2.nspname NOT LIKE $$pg\_%$$ |
||
5974 | AND n3.nspname NOT LIKE $$pg\_%$$ |
||
5975 | '; |
||
5976 | } |
||
5977 | |||
5978 | $sql = " |
||
5979 | SELECT |
||
5980 | c.castsource::pg_catalog.regtype AS castsource, |
||
5981 | c.casttarget::pg_catalog.regtype AS casttarget, |
||
5982 | CASE WHEN c.castfunc=0 THEN NULL |
||
5983 | ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc, |
||
5984 | c.castcontext, |
||
5985 | obj_description(c.oid, 'pg_cast') as castcomment |
||
5986 | FROM |
||
5987 | (pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p ON c.castfunc=p.oid JOIN pg_catalog.pg_namespace n3 ON p.pronamespace=n3.oid), |
||
5988 | pg_catalog.pg_type t1, |
||
5989 | pg_catalog.pg_type t2, |
||
5990 | pg_catalog.pg_namespace n1, |
||
5991 | pg_catalog.pg_namespace n2 |
||
5992 | WHERE |
||
5993 | c.castsource=t1.oid |
||
5994 | AND c.casttarget=t2.oid |
||
5995 | AND t1.typnamespace=n1.oid |
||
5996 | AND t2.typnamespace=n2.oid |
||
5997 | {$where} |
||
5998 | ORDER BY 1, 2 |
||
5999 | "; |
||
6000 | |||
6001 | return $this->selectSet($sql); |
||
6002 | } |
||
6003 | |||
6004 | /** |
||
6005 | * Returns a list of all conversions in the database |
||
6006 | * |
||
6007 | * @return All conversions |
||
6008 | */ |
||
6009 | public function getConversions() |
||
6010 | { |
||
6011 | $c_schema = $this->_schema; |
||
6012 | $this->clean($c_schema); |
||
6013 | $sql = " |
||
6014 | SELECT |
||
6015 | c.conname, |
||
6016 | pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding, |
||
6017 | pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding, |
||
6018 | c.condefault, |
||
6019 | pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment |
||
6020 | FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n |
||
6021 | WHERE n.oid = c.connamespace |
||
6022 | AND n.nspname='{$c_schema}' |
||
6023 | ORDER BY 1; |
||
6024 | "; |
||
6025 | |||
6026 | return $this->selectSet($sql); |
||
6027 | } |
||
6028 | |||
6029 | // Operator Class functions |
||
6030 | |||
6031 | /** |
||
6032 | * Edits a rule on a table OR view |
||
6033 | * |
||
6034 | * @param $name The name of the new rule |
||
6035 | * @param $event SELECT, INSERT, UPDATE or DELETE |
||
6036 | * @param $table Table on which to create the rule |
||
6037 | * @param $where When to execute the rule, '' indicates always |
||
6038 | * @param $instead True if an INSTEAD rule, false otherwise |
||
6039 | * @param $type NOTHING for a do nothing rule, SOMETHING to use given action |
||
6040 | * @param $action The action to take |
||
6041 | * @return \PHPPgAdmin\Database\A 0 success |
||
6042 | */ |
||
6043 | public function setRule($name, $event, $table, $where, $instead, $type, $action) |
||
6044 | { |
||
6045 | return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true); |
||
6046 | } |
||
6047 | |||
6048 | // FTS functions |
||
6049 | |||
6050 | /** |
||
6051 | * Creates a rule |
||
6052 | * |
||
6053 | * @param $name The name of the new rule |
||
6054 | * @param $event SELECT, INSERT, UPDATE or DELETE |
||
6055 | * @param $table Table on which to create the rule |
||
6056 | * @param $where When to execute the rule, '' indicates always |
||
6057 | * @param $instead True if an INSTEAD rule, false otherwise |
||
6058 | * @param $type NOTHING for a do nothing rule, SOMETHING to use given action |
||
6059 | * @param $action The action to take |
||
6060 | * @param bool $replace (optional) True to replace existing rule, false otherwise |
||
6061 | * @return \PHPPgAdmin\Database\A 0 success |
||
6062 | */ |
||
6063 | public function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false) |
||
6064 | { |
||
6065 | $f_schema = $this->_schema; |
||
6066 | $this->fieldClean($f_schema); |
||
6067 | $this->fieldClean($name); |
||
6068 | $this->fieldClean($table); |
||
6069 | if (!in_array($event, $this->rule_events)) { |
||
6070 | return -1; |
||
6071 | } |
||
6072 | |||
6073 | $sql = 'CREATE'; |
||
6074 | if ($replace) { |
||
6075 | $sql .= ' OR REPLACE'; |
||
6076 | } |
||
6077 | |||
6078 | $sql .= " RULE \"{$name}\" AS ON {$event} TO \"{$f_schema}\".\"{$table}\""; |
||
6079 | // Can't escape WHERE clause |
||
6080 | if ($where != '') { |
||
6081 | $sql .= " WHERE {$where}"; |
||
6082 | } |
||
6083 | |||
6084 | $sql .= ' DO'; |
||
6085 | if ($instead) { |
||
6086 | $sql .= ' INSTEAD'; |
||
6087 | } |
||
6088 | |||
6089 | if ($type == 'NOTHING') { |
||
6090 | $sql .= ' NOTHING'; |
||
6091 | } else { |
||
6092 | $sql .= " ({$action})"; |
||
6093 | } |
||
6094 | |||
6095 | return $this->execute($sql); |
||
6096 | } |
||
6097 | |||
6098 | /** |
||
6099 | * Removes a rule from a table OR view |
||
6100 | * |
||
6101 | * @param $rule The rule to drop |
||
6102 | * @param $relation The relation from which to drop |
||
6103 | * @param $cascade True to cascade drop, false to restrict |
||
6104 | * @return \PHPPgAdmin\Database\A 0 success |
||
6105 | */ |
||
6106 | View Code Duplication | public function dropRule($rule, $relation, $cascade) |
|
6107 | { |
||
6108 | $f_schema = $this->_schema; |
||
6109 | $this->fieldClean($f_schema); |
||
6110 | $this->fieldClean($rule); |
||
6111 | $this->fieldClean($relation); |
||
6112 | |||
6113 | $sql = "DROP RULE \"{$rule}\" ON \"{$f_schema}\".\"{$relation}\""; |
||
6114 | if ($cascade) { |
||
6115 | $sql .= ' CASCADE'; |
||
6116 | } |
||
6117 | |||
6118 | return $this->execute($sql); |
||
6119 | } |
||
6120 | |||
6121 | /** |
||
6122 | * Grabs a single trigger |
||
6123 | * |
||
6124 | * @param $table The name of a table whose triggers to retrieve |
||
6125 | * @param $trigger The name of the trigger to retrieve |
||
6126 | * @return A recordset |
||
6127 | */ |
||
6128 | View Code Duplication | public function getTrigger($table, $trigger) |
|
6129 | { |
||
6130 | $c_schema = $this->_schema; |
||
6131 | $this->clean($c_schema); |
||
6132 | $this->clean($table); |
||
6133 | $this->clean($trigger); |
||
6134 | |||
6135 | $sql = " |
||
6136 | SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c |
||
6137 | WHERE t.tgrelid=c.oid AND c.relname='{$table}' AND t.tgname='{$trigger}' |
||
6138 | AND c.relnamespace=( |
||
6139 | SELECT oid FROM pg_catalog.pg_namespace |
||
6140 | WHERE nspname='{$c_schema}')"; |
||
6141 | |||
6142 | return $this->selectSet($sql); |
||
6143 | } |
||
6144 | |||
6145 | /** |
||
6146 | * A helper function for getTriggers that translates |
||
6147 | * an array of attribute numbers to an array of field names. |
||
6148 | * Note: Only needed for pre-7.4 servers, this function is deprecated |
||
6149 | * |
||
6150 | * @param $trigger An array containing fields from the trigger table |
||
6151 | * @return The trigger definition string |
||
6152 | */ |
||
6153 | public function getTriggerDef($trigger) |
||
6154 | { |
||
6155 | $this->fieldArrayClean($trigger); |
||
6156 | // Constants to figure out tgtype |
||
6157 | if (!defined('TRIGGER_TYPE_ROW')) { |
||
6158 | define('TRIGGER_TYPE_ROW', 1 << 0); |
||
6159 | } |
||
6160 | |||
6161 | if (!defined('TRIGGER_TYPE_BEFORE')) { |
||
6162 | define('TRIGGER_TYPE_BEFORE', 1 << 1); |
||
6163 | } |
||
6164 | |||
6165 | if (!defined('TRIGGER_TYPE_INSERT')) { |
||
6166 | define('TRIGGER_TYPE_INSERT', 1 << 2); |
||
6167 | } |
||
6168 | |||
6169 | if (!defined('TRIGGER_TYPE_DELETE')) { |
||
6170 | define('TRIGGER_TYPE_DELETE', 1 << 3); |
||
6171 | } |
||
6172 | |||
6173 | if (!defined('TRIGGER_TYPE_UPDATE')) { |
||
6174 | define('TRIGGER_TYPE_UPDATE', 1 << 4); |
||
6175 | } |
||
6176 | |||
6177 | $trigger['tgisconstraint'] = $this->phpBool($trigger['tgisconstraint']); |
||
6178 | $trigger['tgdeferrable'] = $this->phpBool($trigger['tgdeferrable']); |
||
6179 | $trigger['tginitdeferred'] = $this->phpBool($trigger['tginitdeferred']); |
||
6180 | |||
6181 | // Constraint trigger or normal trigger |
||
6182 | if ($trigger['tgisconstraint']) { |
||
6183 | $tgdef = 'CREATE CONSTRAINT TRIGGER '; |
||
6184 | } else { |
||
6185 | $tgdef = 'CREATE TRIGGER '; |
||
6186 | } |
||
6187 | |||
6188 | $tgdef .= "\"{$trigger['tgname']}\" "; |
||
6189 | |||
6190 | // Trigger type |
||
6191 | $findx = 0; |
||
6192 | if (($trigger['tgtype'] & TRIGGER_TYPE_BEFORE) == TRIGGER_TYPE_BEFORE) { |
||
6193 | $tgdef .= 'BEFORE'; |
||
6194 | } else { |
||
6195 | $tgdef .= 'AFTER'; |
||
6196 | } |
||
6197 | |||
6198 | if (($trigger['tgtype'] & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT) { |
||
6199 | $tgdef .= ' INSERT'; |
||
6200 | $findx++; |
||
6201 | } |
||
6202 | View Code Duplication | if (($trigger['tgtype'] & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) { |
|
6203 | if ($findx > 0) { |
||
6204 | $tgdef .= ' OR DELETE'; |
||
6205 | } else { |
||
6206 | $tgdef .= ' DELETE'; |
||
6207 | $findx++; |
||
6208 | } |
||
6209 | } |
||
6210 | View Code Duplication | if (($trigger['tgtype'] & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) { |
|
6211 | if ($findx > 0) { |
||
6212 | $tgdef .= ' OR UPDATE'; |
||
6213 | } else { |
||
6214 | $tgdef .= ' UPDATE'; |
||
6215 | } |
||
6216 | } |
||
6217 | |||
6218 | $f_schema = $this->_schema; |
||
6219 | $this->fieldClean($f_schema); |
||
6220 | // Table name |
||
6221 | $tgdef .= " ON \"{$f_schema}\".\"{$trigger['relname']}\" "; |
||
6222 | |||
6223 | // Deferrability |
||
6224 | if ($trigger['tgisconstraint']) { |
||
6225 | if ($trigger['tgconstrrelid'] != 0) { |
||
6226 | // Assume constrelname is not null |
||
6227 | $tgdef .= " FROM \"{$trigger['tgconstrrelname']}\" "; |
||
6228 | } |
||
6229 | if (!$trigger['tgdeferrable']) { |
||
6230 | $tgdef .= 'NOT '; |
||
6231 | } |
||
6232 | |||
6233 | $tgdef .= 'DEFERRABLE INITIALLY '; |
||
6234 | if ($trigger['tginitdeferred']) { |
||
6235 | $tgdef .= 'DEFERRED '; |
||
6236 | } else { |
||
6237 | $tgdef .= 'IMMEDIATE '; |
||
6238 | } |
||
6239 | } |
||
6240 | |||
6241 | // Row or statement |
||
6242 | if ($trigger['tgtype'] & TRIGGER_TYPE_ROW == TRIGGER_TYPE_ROW) { |
||
6243 | $tgdef .= 'FOR EACH ROW '; |
||
6244 | } else { |
||
6245 | $tgdef .= 'FOR EACH STATEMENT '; |
||
6246 | } |
||
6247 | |||
6248 | // Execute procedure |
||
6249 | $tgdef .= "EXECUTE PROCEDURE \"{$trigger['tgfname']}\"("; |
||
6250 | |||
6251 | // Parameters |
||
6252 | // Escape null characters |
||
6253 | $v = addcslashes($trigger['tgargs'], "\0"); |
||
6254 | // Split on escaped null characters |
||
6255 | $params = explode('\\000', $v); |
||
6256 | for ($findx = 0; $findx < $trigger['tgnargs']; $findx++) { |
||
6257 | $param = "'" . str_replace('\'', '\\\'', $params[$findx]) . "'"; |
||
6258 | $tgdef .= $param; |
||
6259 | if ($findx < ($trigger['tgnargs'] - 1)) { |
||
6260 | $tgdef .= ', '; |
||
6261 | } |
||
6262 | } |
||
6263 | |||
6264 | // Finish it off |
||
6265 | $tgdef .= ')'; |
||
6266 | |||
6267 | return $tgdef; |
||
6268 | } |
||
6269 | |||
6270 | /** |
||
6271 | * Returns a list of all functions that can be used in triggers |
||
6272 | */ |
||
6273 | public function getTriggerFunctions() |
||
6274 | { |
||
6275 | return $this->getFunctions(true, 'trigger'); |
||
6276 | } |
||
6277 | |||
6278 | /** |
||
6279 | * Returns a list of all functions in the database |
||
6280 | * |
||
6281 | * @param bool|\PHPPgAdmin\Database\If $all If true, will find all available functions, if false just those in search path |
||
6282 | * @param $type If not null, will find all functions with return value = type |
||
6283 | * @return \PHPPgAdmin\Database\All functions |
||
6284 | */ |
||
6285 | public function getFunctions($all = false, $type = null) |
||
6286 | { |
||
6287 | if ($all) { |
||
6288 | $where = 'pg_catalog.pg_function_is_visible(p.oid)'; |
||
6289 | $distinct = 'DISTINCT ON (p.proname)'; |
||
6290 | |||
6291 | if ($type) { |
||
6292 | $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') "; |
||
6293 | } |
||
6294 | } else { |
||
6295 | $c_schema = $this->_schema; |
||
6296 | $this->clean($c_schema); |
||
6297 | $where = "n.nspname = '{$c_schema}'"; |
||
6298 | $distinct = ''; |
||
6299 | } |
||
6300 | |||
6301 | $sql = " |
||
6302 | SELECT |
||
6303 | {$distinct} |
||
6304 | p.oid AS prooid, |
||
6305 | p.proname, |
||
6306 | p.proretset, |
||
6307 | pg_catalog.format_type(p.prorettype, NULL) AS proresult, |
||
6308 | pg_catalog.oidvectortypes(p.proargtypes) AS proarguments, |
||
6309 | pl.lanname AS prolanguage, |
||
6310 | pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment, |
||
6311 | p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto, |
||
6312 | CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns, |
||
6313 | u.usename AS proowner |
||
6314 | FROM pg_catalog.pg_proc p |
||
6315 | INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace |
||
6316 | INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang |
||
6317 | LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner |
||
6318 | WHERE NOT p.proisagg |
||
6319 | AND {$where} |
||
6320 | ORDER BY p.proname, proresult |
||
6321 | "; |
||
6322 | |||
6323 | return $this->selectSet($sql); |
||
6324 | } |
||
6325 | |||
6326 | /** |
||
6327 | * Creates a trigger |
||
6328 | * |
||
6329 | * @param $tgname The name of the trigger to create |
||
6330 | * @param $table The name of the table |
||
6331 | * @param $tgproc The function to execute |
||
6332 | * @param $tgtime BEFORE or AFTER |
||
6333 | * @param $tgevent Event |
||
6334 | * @param $tgfrequency |
||
6335 | * @param $tgargs The function arguments |
||
6336 | * @return \PHPPgAdmin\Database\A 0 success |
||
6337 | */ |
||
6338 | public function createTrigger($tgname, $table, $tgproc, $tgtime, $tgevent, $tgfrequency, $tgargs) |
||
6339 | { |
||
6340 | $f_schema = $this->_schema; |
||
6341 | $this->fieldClean($f_schema); |
||
6342 | $this->fieldClean($tgname); |
||
6343 | $this->fieldClean($table); |
||
6344 | $this->fieldClean($tgproc); |
||
6345 | |||
6346 | /* No Statement Level Triggers in PostgreSQL (by now) */ |
||
6347 | $sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime} |
||
6348 | {$tgevent} ON \"{$f_schema}\".\"{$table}\" |
||
6349 | FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})"; |
||
6350 | |||
6351 | return $this->execute($sql); |
||
6352 | } |
||
6353 | |||
6354 | /** |
||
6355 | * Alters a trigger |
||
6356 | * |
||
6357 | * @param $table The name of the table containing the trigger |
||
6358 | * @param $trigger The name of the trigger to alter |
||
6359 | * @param $name The new name for the trigger |
||
6360 | * @return \PHPPgAdmin\Database\A 0 success |
||
6361 | */ |
||
6362 | public function alterTrigger($table, $trigger, $name) |
||
6363 | { |
||
6364 | $f_schema = $this->_schema; |
||
6365 | $this->fieldClean($f_schema); |
||
6366 | $this->fieldClean($table); |
||
6367 | $this->fieldClean($trigger); |
||
6368 | $this->fieldClean($name); |
||
6369 | |||
6370 | $sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$f_schema}\".\"{$table}\" RENAME TO \"{$name}\""; |
||
6371 | |||
6372 | return $this->execute($sql); |
||
6373 | } |
||
6374 | |||
6375 | /** |
||
6376 | * Drops a trigger |
||
6377 | * |
||
6378 | * @param $tgname The name of the trigger to drop |
||
6379 | * @param $table The table from which to drop the trigger |
||
6380 | * @param $cascade True to cascade drop, false to restrict |
||
6381 | * @return \PHPPgAdmin\Database\A 0 success |
||
6382 | */ |
||
6383 | View Code Duplication | public function dropTrigger($tgname, $table, $cascade) |
|
6384 | { |
||
6385 | $f_schema = $this->_schema; |
||
6386 | $this->fieldClean($f_schema); |
||
6387 | $this->fieldClean($tgname); |
||
6388 | $this->fieldClean($table); |
||
6389 | |||
6390 | $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$f_schema}\".\"{$table}\""; |
||
6391 | if ($cascade) { |
||
6392 | $sql .= ' CASCADE'; |
||
6393 | } |
||
6394 | |||
6395 | return $this->execute($sql); |
||
6396 | } |
||
6397 | |||
6398 | /** |
||
6399 | * Enables a trigger |
||
6400 | * |
||
6401 | * @param $tgname The name of the trigger to enable |
||
6402 | * @param $table The table in which to enable the trigger |
||
6403 | * @return \PHPPgAdmin\Database\A 0 success |
||
6404 | */ |
||
6405 | View Code Duplication | public function enableTrigger($tgname, $table) |
|
6406 | { |
||
6407 | $f_schema = $this->_schema; |
||
6408 | $this->fieldClean($f_schema); |
||
6409 | $this->fieldClean($tgname); |
||
6410 | $this->fieldClean($table); |
||
6411 | |||
6412 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ENABLE TRIGGER \"{$tgname}\""; |
||
6413 | |||
6414 | return $this->execute($sql); |
||
6415 | } |
||
6416 | |||
6417 | /** |
||
6418 | * Disables a trigger |
||
6419 | * |
||
6420 | * @param $tgname The name of the trigger to disable |
||
6421 | * @param $table The table in which to disable the trigger |
||
6422 | * @return \PHPPgAdmin\Database\A 0 success |
||
6423 | */ |
||
6424 | View Code Duplication | public function disableTrigger($tgname, $table) |
|
6425 | { |
||
6426 | $f_schema = $this->_schema; |
||
6427 | $this->fieldClean($f_schema); |
||
6428 | $this->fieldClean($tgname); |
||
6429 | $this->fieldClean($table); |
||
6430 | |||
6431 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DISABLE TRIGGER \"{$tgname}\""; |
||
6432 | |||
6433 | return $this->execute($sql); |
||
6434 | } |
||
6435 | |||
6436 | /** |
||
6437 | * Returns a list of all operators in the database |
||
6438 | * |
||
6439 | * @return All operators |
||
6440 | */ |
||
6441 | public function getOperators() |
||
6442 | { |
||
6443 | $c_schema = $this->_schema; |
||
6444 | $this->clean($c_schema); |
||
6445 | // We stick with the subselects here, as you cannot ORDER BY a regtype |
||
6446 | $sql = " |
||
6447 | SELECT |
||
6448 | po.oid, po.oprname, |
||
6449 | (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname, |
||
6450 | (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname, |
||
6451 | po.oprresult::pg_catalog.regtype AS resultname, |
||
6452 | pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment |
||
6453 | FROM |
||
6454 | pg_catalog.pg_operator po |
||
6455 | WHERE |
||
6456 | po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}') |
||
6457 | ORDER BY |
||
6458 | po.oprname, oprleftname, oprrightname |
||
6459 | "; |
||
6460 | |||
6461 | return $this->selectSet($sql); |
||
6462 | } |
||
6463 | |||
6464 | /** |
||
6465 | * Drops an operator |
||
6466 | * |
||
6467 | * @param $operator_oid The OID of the operator to drop |
||
6468 | * @param $cascade True to cascade drop, false to restrict |
||
6469 | * @return \PHPPgAdmin\Database\A 0 success |
||
6470 | */ |
||
6471 | public function dropOperator($operator_oid, $cascade) |
||
6472 | { |
||
6473 | // Function comes in with $object as operator OID |
||
6474 | $opr = $this->getOperator($operator_oid); |
||
6475 | $f_schema = $this->_schema; |
||
6476 | $this->fieldClean($f_schema); |
||
6477 | $this->fieldClean($opr->fields['oprname']); |
||
6478 | |||
6479 | $sql = "DROP OPERATOR \"{$f_schema}\".{$opr->fields['oprname']} ("; |
||
6480 | // Quoting or formatting here??? |
||
6481 | if ($opr->fields['oprleftname'] !== null) { |
||
6482 | $sql .= $opr->fields['oprleftname'] . ', '; |
||
6483 | } else { |
||
6484 | $sql .= 'NONE, '; |
||
6485 | } |
||
6486 | |||
6487 | if ($opr->fields['oprrightname'] !== null) { |
||
6488 | $sql .= $opr->fields['oprrightname'] . ')'; |
||
6489 | } else { |
||
6490 | $sql .= 'NONE)'; |
||
6491 | } |
||
6492 | |||
6493 | if ($cascade) { |
||
6494 | $sql .= ' CASCADE'; |
||
6495 | } |
||
6496 | |||
6497 | return $this->execute($sql); |
||
6498 | } |
||
6499 | |||
6500 | /** |
||
6501 | * Returns all details for a particular operator |
||
6502 | * |
||
6503 | * @param $operator_oid The oid of the operator |
||
6504 | * @return Function info |
||
6505 | */ |
||
6506 | public function getOperator($operator_oid) |
||
6507 | { |
||
6508 | $this->clean($operator_oid); |
||
6509 | |||
6510 | $sql = " |
||
6511 | SELECT |
||
6512 | po.oid, po.oprname, |
||
6513 | oprleft::pg_catalog.regtype AS oprleftname, |
||
6514 | oprright::pg_catalog.regtype AS oprrightname, |
||
6515 | oprresult::pg_catalog.regtype AS resultname, |
||
6516 | po.oprcanhash, |
||
6517 | oprcanmerge, |
||
6518 | oprcom::pg_catalog.regoperator AS oprcom, |
||
6519 | oprnegate::pg_catalog.regoperator AS oprnegate, |
||
6520 | po.oprcode::pg_catalog.regproc AS oprcode, |
||
6521 | po.oprrest::pg_catalog.regproc AS oprrest, |
||
6522 | po.oprjoin::pg_catalog.regproc AS oprjoin |
||
6523 | FROM |
||
6524 | pg_catalog.pg_operator po |
||
6525 | WHERE |
||
6526 | po.oid='{$operator_oid}' |
||
6527 | "; |
||
6528 | |||
6529 | return $this->selectSet($sql); |
||
6530 | } |
||
6531 | |||
6532 | /** |
||
6533 | * Gets all opclasses |
||
6534 | * |
||
6535 | * @return A recordset |
||
6536 | */ |
||
6537 | |||
6538 | public function getOpClasses() |
||
6539 | { |
||
6540 | $c_schema = $this->_schema; |
||
6541 | $this->clean($c_schema); |
||
6542 | $sql = " |
||
6543 | SELECT |
||
6544 | pa.amname, po.opcname, |
||
6545 | po.opcintype::pg_catalog.regtype AS opcintype, |
||
6546 | po.opcdefault, |
||
6547 | pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment |
||
6548 | FROM |
||
6549 | pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn |
||
6550 | WHERE |
||
6551 | po.opcmethod=pa.oid |
||
6552 | AND po.opcnamespace=pn.oid |
||
6553 | AND pn.nspname='{$c_schema}' |
||
6554 | ORDER BY 1,2 |
||
6555 | "; |
||
6556 | |||
6557 | return $this->selectSet($sql); |
||
6558 | } |
||
6559 | |||
6560 | /** |
||
6561 | * Creates a new FTS configuration. |
||
6562 | * |
||
6563 | * @param string $cfgname The name of the FTS configuration to create |
||
6564 | * @param string $parser The parser to be used in new FTS configuration |
||
6565 | * @param string $template The existing FTS configuration to be used as template for the new one |
||
6566 | * @param string $comment If omitted, defaults to nothing |
||
6567 | * @return bool|int 0 success |
||
6568 | * @internal param string $locale Locale of the FTS configuration |
||
6569 | * @internal param string $withmap Should we copy whole map of existing FTS configuration to the new one |
||
6570 | * @internal param string $makeDefault Should this configuration be the default for locale given |
||
6571 | */ |
||
6572 | public function createFtsConfiguration($cfgname, $parser = '', $template = '', $comment = '') |
||
6573 | { |
||
6574 | $f_schema = $this->_schema; |
||
6575 | $this->fieldClean($f_schema); |
||
6576 | $this->fieldClean($cfgname); |
||
6577 | |||
6578 | $sql = "CREATE TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$cfgname}\" ("; |
||
6579 | View Code Duplication | if ($parser != '') { |
|
6580 | $this->fieldClean($parser['schema']); |
||
6581 | $this->fieldClean($parser['parser']); |
||
6582 | $parser = "\"{$parser['schema']}\".\"{$parser['parser']}\""; |
||
6583 | $sql .= " PARSER = {$parser}"; |
||
6584 | } |
||
6585 | View Code Duplication | if ($template != '') { |
|
6586 | $this->fieldClean($template['schema']); |
||
6587 | $this->fieldClean($template['name']); |
||
6588 | $sql .= " COPY = \"{$template['schema']}\".\"{$template['name']}\""; |
||
6589 | } |
||
6590 | $sql .= ')'; |
||
6591 | |||
6592 | if ($comment != '') { |
||
6593 | $status = $this->beginTransaction(); |
||
6594 | if ($status != 0) { |
||
6595 | return -1; |
||
6596 | } |
||
6597 | } |
||
6598 | |||
6599 | // Create the FTS configuration |
||
6600 | $status = $this->execute($sql); |
||
6601 | if ($status != 0) { |
||
6602 | $this->rollbackTransaction(); |
||
6603 | |||
6604 | return -1; |
||
6605 | } |
||
6606 | |||
6607 | // Set the comment |
||
6608 | View Code Duplication | if ($comment != '') { |
|
6609 | $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment); |
||
6610 | if ($status != 0) { |
||
6611 | $this->rollbackTransaction(); |
||
6612 | |||
6613 | return -1; |
||
6614 | } |
||
6615 | |||
6616 | return $this->endTransaction(); |
||
6617 | } |
||
6618 | |||
6619 | return 0; |
||
6620 | } |
||
6621 | |||
6622 | // Language functions |
||
6623 | |||
6624 | /** |
||
6625 | * Returns available FTS configurations |
||
6626 | * |
||
6627 | * @param bool|\PHPPgAdmin\Database\if $all if false, returns schema qualified FTS confs |
||
6628 | * @return \PHPPgAdmin\Database\A recordset |
||
6629 | */ |
||
6630 | public function getFtsConfigurations($all = true) |
||
6631 | { |
||
6632 | $c_schema = $this->_schema; |
||
6633 | $this->clean($c_schema); |
||
6634 | $sql = " |
||
6635 | SELECT |
||
6636 | n.nspname as schema, |
||
6637 | c.cfgname as name, |
||
6638 | pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment |
||
6639 | FROM |
||
6640 | pg_catalog.pg_ts_config c |
||
6641 | JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace |
||
6642 | WHERE |
||
6643 | pg_catalog.pg_ts_config_is_visible(c.oid)"; |
||
6644 | |||
6645 | if (!$all) { |
||
6646 | $sql .= " AND n.nspname='{$c_schema}'\n"; |
||
6647 | } |
||
6648 | |||
6649 | $sql .= 'ORDER BY name'; |
||
6650 | |||
6651 | return $this->selectSet($sql); |
||
6652 | } |
||
6653 | |||
6654 | // Aggregate functions |
||
6655 | |||
6656 | /** |
||
6657 | * Returns the map of FTS configuration given |
||
6658 | * (list of mappings (tokens) and their processing dictionaries) |
||
6659 | * |
||
6660 | * @param string $ftscfg Name of the FTS configuration |
||
6661 | * |
||
6662 | * @return RecordSet |
||
6663 | */ |
||
6664 | public function getFtsConfigurationMap($ftscfg) |
||
6665 | { |
||
6666 | $c_schema = $this->_schema; |
||
6667 | $this->clean($c_schema); |
||
6668 | $this->fieldClean($ftscfg); |
||
6669 | |||
6670 | $oidSet = $this->selectSet("SELECT c.oid |
||
6671 | FROM pg_catalog.pg_ts_config AS c |
||
6672 | LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.cfgnamespace) |
||
6673 | WHERE c.cfgname = '{$ftscfg}' |
||
6674 | AND n.nspname='{$c_schema}'"); |
||
6675 | |||
6676 | $oid = $oidSet->fields['oid']; |
||
6677 | |||
6678 | $sql = " |
||
6679 | SELECT |
||
6680 | (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name, |
||
6681 | (SELECT t.description FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS description, |
||
6682 | c.cfgname AS cfgname, n.nspname ||'.'|| d.dictname as dictionaries |
||
6683 | FROM |
||
6684 | pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d, |
||
6685 | pg_catalog.pg_namespace n |
||
6686 | WHERE |
||
6687 | c.oid = {$oid} |
||
6688 | AND m.mapcfg = c.oid |
||
6689 | AND m.mapdict = d.oid |
||
6690 | AND d.dictnamespace = n.oid |
||
6691 | ORDER BY name |
||
6692 | "; |
||
6693 | |||
6694 | return $this->selectSet($sql); |
||
6695 | } |
||
6696 | |||
6697 | /** |
||
6698 | * Returns FTS parsers available |
||
6699 | * |
||
6700 | * @param bool|\PHPPgAdmin\Database\if $all if false, return only Parsers from the current schema |
||
6701 | * @return \PHPPgAdmin\Database\RecordSet |
||
6702 | */ |
||
6703 | public function getFtsParsers($all = true) |
||
6704 | { |
||
6705 | $c_schema = $this->_schema; |
||
6706 | $this->clean($c_schema); |
||
6707 | $sql = " |
||
6708 | SELECT |
||
6709 | n.nspname as schema, |
||
6710 | p.prsname as name, |
||
6711 | pg_catalog.obj_description(p.oid, 'pg_ts_parser') as comment |
||
6712 | FROM pg_catalog.pg_ts_parser p |
||
6713 | LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = p.prsnamespace) |
||
6714 | WHERE pg_catalog.pg_ts_parser_is_visible(p.oid)"; |
||
6715 | |||
6716 | if (!$all) { |
||
6717 | $sql .= " AND n.nspname='{$c_schema}'\n"; |
||
6718 | } |
||
6719 | |||
6720 | $sql .= 'ORDER BY name'; |
||
6721 | |||
6722 | return $this->selectSet($sql); |
||
6723 | } |
||
6724 | |||
6725 | /** |
||
6726 | * Returns FTS dictionaries available |
||
6727 | * |
||
6728 | * @param bool|\PHPPgAdmin\Database\if $all if false, return only Dics from the current schema |
||
6729 | * @return \PHPPgAdmin\Database\RecordSet |
||
6730 | */ |
||
6731 | public function getFtsDictionaries($all = true) |
||
6732 | { |
||
6733 | $c_schema = $this->_schema; |
||
6734 | $this->clean($c_schema); |
||
6735 | $sql = " |
||
6736 | SELECT |
||
6737 | n.nspname as schema, d.dictname as name, |
||
6738 | pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment |
||
6739 | FROM pg_catalog.pg_ts_dict d |
||
6740 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace |
||
6741 | WHERE pg_catalog.pg_ts_dict_is_visible(d.oid)"; |
||
6742 | |||
6743 | if (!$all) { |
||
6744 | $sql .= " AND n.nspname='{$c_schema}'\n"; |
||
6745 | } |
||
6746 | |||
6747 | $sql .= 'ORDER BY name;'; |
||
6748 | |||
6749 | return $this->selectSet($sql); |
||
6750 | } |
||
6751 | |||
6752 | /** |
||
6753 | * Returns all FTS dictionary templates available |
||
6754 | */ |
||
6755 | public function getFtsDictionaryTemplates() |
||
6756 | { |
||
6757 | $sql = " |
||
6758 | SELECT |
||
6759 | n.nspname as schema, |
||
6760 | t.tmplname as name, |
||
6761 | ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname |
||
6762 | FROM pg_catalog.pg_proc p |
||
6763 | LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace |
||
6764 | WHERE t.tmplinit = p.oid ) AS init, |
||
6765 | ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname |
||
6766 | FROM pg_catalog.pg_proc p |
||
6767 | LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace |
||
6768 | WHERE t.tmpllexize = p.oid ) AS lexize, |
||
6769 | pg_catalog.obj_description(t.oid, 'pg_ts_template') as comment |
||
6770 | FROM pg_catalog.pg_ts_template t |
||
6771 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace |
||
6772 | WHERE pg_catalog.pg_ts_template_is_visible(t.oid) |
||
6773 | ORDER BY name;"; |
||
6774 | |||
6775 | return $this->selectSet($sql); |
||
6776 | } |
||
6777 | |||
6778 | /** |
||
6779 | * Drops FTS coniguration |
||
6780 | * |
||
6781 | * @param $ftscfg The configuration's name |
||
6782 | * @param $cascade Cascade to dependenced objects |
||
6783 | * @return \PHPPgAdmin\Database\A 0 on success |
||
6784 | */ |
||
6785 | View Code Duplication | public function dropFtsConfiguration($ftscfg, $cascade) |
|
6786 | { |
||
6787 | $f_schema = $this->_schema; |
||
6788 | $this->fieldClean($f_schema); |
||
6789 | $this->fieldClean($ftscfg); |
||
6790 | |||
6791 | $sql = "DROP TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\""; |
||
6792 | if ($cascade) { |
||
6793 | $sql .= ' CASCADE'; |
||
6794 | } |
||
6795 | |||
6796 | return $this->execute($sql); |
||
6797 | } |
||
6798 | |||
6799 | /** |
||
6800 | * Drops FTS dictionary |
||
6801 | * |
||
6802 | * @param $ftsdict The dico's name |
||
6803 | * @param $cascade Cascade to dependenced objects |
||
6804 | * @return \PHPPgAdmin\Database\A 0 on success |
||
6805 | * @todo Support of dictionary templates dropping |
||
6806 | */ |
||
6807 | View Code Duplication | public function dropFtsDictionary($ftsdict, $cascade) |
|
6808 | { |
||
6809 | $f_schema = $this->_schema; |
||
6810 | $this->fieldClean($f_schema); |
||
6811 | $this->fieldClean($ftsdict); |
||
6812 | |||
6813 | $sql = 'DROP TEXT SEARCH DICTIONARY'; |
||
6814 | $sql .= " \"{$f_schema}\".\"{$ftsdict}\""; |
||
6815 | if ($cascade) { |
||
6816 | $sql .= ' CASCADE'; |
||
6817 | } |
||
6818 | |||
6819 | return $this->execute($sql); |
||
6820 | } |
||
6821 | |||
6822 | /** |
||
6823 | * Alters FTS configuration |
||
6824 | * |
||
6825 | * @param $cfgname The conf's name |
||
6826 | * @param $comment A comment on for the conf |
||
6827 | * @param $name The new conf name |
||
6828 | * @return bool|int 0 on success |
||
6829 | */ |
||
6830 | View Code Duplication | public function updateFtsConfiguration($cfgname, $comment, $name) |
|
6831 | { |
||
6832 | $status = $this->beginTransaction(); |
||
6833 | if ($status != 0) { |
||
6834 | $this->rollbackTransaction(); |
||
6835 | |||
6836 | return -1; |
||
6837 | } |
||
6838 | |||
6839 | $this->fieldClean($cfgname); |
||
6840 | |||
6841 | $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment); |
||
6842 | if ($status != 0) { |
||
6843 | $this->rollbackTransaction(); |
||
6844 | |||
6845 | return -1; |
||
6846 | } |
||
6847 | |||
6848 | // Only if the name has changed |
||
6849 | if ($name != $cfgname) { |
||
6850 | $f_schema = $this->_schema; |
||
6851 | $this->fieldClean($f_schema); |
||
6852 | $this->fieldClean($name); |
||
6853 | |||
6854 | $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$cfgname}\" RENAME TO \"{$name}\""; |
||
6855 | $status = $this->execute($sql); |
||
6856 | if ($status != 0) { |
||
6857 | $this->rollbackTransaction(); |
||
6858 | |||
6859 | return -1; |
||
6860 | } |
||
6861 | } |
||
6862 | |||
6863 | return $this->endTransaction(); |
||
6864 | } |
||
6865 | |||
6866 | /** |
||
6867 | * Creates a new FTS dictionary or FTS dictionary template. |
||
6868 | * |
||
6869 | * @param string $dictname The name of the FTS dictionary to create |
||
6870 | * @param boolean $isTemplate Flag whether we create usual dictionary or dictionary template |
||
6871 | * @param string $template The existing FTS dictionary to be used as template for the new one |
||
6872 | * @param string $lexize The name of the function, which does transformation of input word |
||
6873 | * @param string $init The name of the function, which initializes dictionary |
||
6874 | * @param string $option Usually, it stores various options required for the dictionary |
||
6875 | * @param string $comment If omitted, defaults to nothing |
||
6876 | * @return bool|int 0 success |
||
6877 | */ |
||
6878 | public function createFtsDictionary( |
||
6879 | $dictname, |
||
6880 | $isTemplate = false, |
||
6881 | $template = '', |
||
6882 | $lexize = '', |
||
6883 | $init = '', |
||
6884 | $option = '', |
||
6885 | $comment = '' |
||
6886 | ) { |
||
6887 | $f_schema = $this->_schema; |
||
6888 | $this->fieldClean($f_schema); |
||
6889 | $this->fieldClean($dictname); |
||
6890 | $this->fieldClean($template); |
||
6891 | $this->fieldClean($lexize); |
||
6892 | $this->fieldClean($init); |
||
6893 | $this->fieldClean($option); |
||
6894 | |||
6895 | $sql = 'CREATE TEXT SEARCH'; |
||
6896 | if ($isTemplate) { |
||
6897 | $sql .= " TEMPLATE \"{$f_schema}\".\"{$dictname}\" ("; |
||
6898 | if ($lexize != '') { |
||
6899 | $sql .= " LEXIZE = {$lexize}"; |
||
6900 | } |
||
6901 | |||
6902 | if ($init != '') { |
||
6903 | $sql .= ", INIT = {$init}"; |
||
6904 | } |
||
6905 | |||
6906 | $sql .= ')'; |
||
6907 | $whatToComment = 'TEXT SEARCH TEMPLATE'; |
||
6908 | } else { |
||
6909 | $sql .= " DICTIONARY \"{$f_schema}\".\"{$dictname}\" ("; |
||
6910 | View Code Duplication | if ($template != '') { |
|
6911 | $this->fieldClean($template['schema']); |
||
6912 | $this->fieldClean($template['name']); |
||
6913 | $template = "\"{$template['schema']}\".\"{$template['name']}\""; |
||
6914 | |||
6915 | $sql .= " TEMPLATE = {$template}"; |
||
6916 | } |
||
6917 | if ($option != '') { |
||
6918 | $sql .= ", {$option}"; |
||
6919 | } |
||
6920 | |||
6921 | $sql .= ')'; |
||
6922 | $whatToComment = 'TEXT SEARCH DICTIONARY'; |
||
6923 | } |
||
6924 | |||
6925 | /* if comment, begin a transaction to |
||
6926 | * run both commands */ |
||
6927 | if ($comment != '') { |
||
6928 | $status = $this->beginTransaction(); |
||
6929 | if ($status != 0) { |
||
6930 | return -1; |
||
6931 | } |
||
6932 | } |
||
6933 | |||
6934 | // Create the FTS dictionary |
||
6935 | $status = $this->execute($sql); |
||
6936 | if ($status != 0) { |
||
6937 | $this->rollbackTransaction(); |
||
6938 | |||
6939 | return -1; |
||
6940 | } |
||
6941 | |||
6942 | // Set the comment |
||
6943 | View Code Duplication | if ($comment != '') { |
|
6944 | $status = $this->setComment($whatToComment, $dictname, '', $comment); |
||
6945 | if ($status != 0) { |
||
6946 | $this->rollbackTransaction(); |
||
6947 | |||
6948 | return -1; |
||
6949 | } |
||
6950 | } |
||
6951 | |||
6952 | return $this->endTransaction(); |
||
6953 | } |
||
6954 | |||
6955 | // Role, User/Group functions |
||
6956 | |||
6957 | /** |
||
6958 | * Alters FTS dictionary or dictionary template |
||
6959 | * |
||
6960 | * @param $dictname The dico's name |
||
6961 | * @param $comment The comment |
||
6962 | * @param $name The new dico's name |
||
6963 | * @return bool|int 0 on success |
||
6964 | */ |
||
6965 | View Code Duplication | public function updateFtsDictionary($dictname, $comment, $name) |
|
6966 | { |
||
6967 | $status = $this->beginTransaction(); |
||
6968 | if ($status != 0) { |
||
6969 | $this->rollbackTransaction(); |
||
6970 | |||
6971 | return -1; |
||
6972 | } |
||
6973 | |||
6974 | $this->fieldClean($dictname); |
||
6975 | $status = $this->setComment('TEXT SEARCH DICTIONARY', $dictname, '', $comment); |
||
6976 | if ($status != 0) { |
||
6977 | $this->rollbackTransaction(); |
||
6978 | |||
6979 | return -1; |
||
6980 | } |
||
6981 | |||
6982 | // Only if the name has changed |
||
6983 | if ($name != $dictname) { |
||
6984 | $f_schema = $this->_schema; |
||
6985 | $this->fieldClean($f_schema); |
||
6986 | $this->fieldClean($name); |
||
6987 | |||
6988 | $sql = "ALTER TEXT SEARCH DICTIONARY \"{$f_schema}\".\"{$dictname}\" RENAME TO \"{$name}\""; |
||
6989 | $status = $this->execute($sql); |
||
6990 | if ($status != 0) { |
||
6991 | $this->rollbackTransaction(); |
||
6992 | |||
6993 | return -1; |
||
6994 | } |
||
6995 | } |
||
6996 | |||
6997 | return $this->endTransaction(); |
||
6998 | } |
||
6999 | |||
7000 | /** |
||
7001 | * Return all information relating to a FTS dictionary |
||
7002 | * |
||
7003 | * @param $ftsdict The name of the FTS dictionary |
||
7004 | * |
||
7005 | * @return RecordSet of FTS dictionary information |
||
7006 | */ |
||
7007 | public function getFtsDictionaryByName($ftsdict) |
||
7008 | { |
||
7009 | $c_schema = $this->_schema; |
||
7010 | $this->clean($c_schema); |
||
7011 | $this->clean($ftsdict); |
||
7012 | |||
7013 | $sql = "SELECT |
||
7014 | n.nspname as schema, |
||
7015 | d.dictname as name, |
||
7016 | ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM |
||
7017 | pg_catalog.pg_ts_template t |
||
7018 | LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace |
||
7019 | WHERE d.dicttemplate = t.oid ) AS template, |
||
7020 | d.dictinitoption as init, |
||
7021 | pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment |
||
7022 | FROM pg_catalog.pg_ts_dict d |
||
7023 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace |
||
7024 | WHERE d.dictname = '{$ftsdict}' |
||
7025 | AND pg_catalog.pg_ts_dict_is_visible(d.oid) |
||
7026 | AND n.nspname='{$c_schema}' |
||
7027 | ORDER BY name"; |
||
7028 | |||
7029 | return $this->selectSet($sql); |
||
7030 | } |
||
7031 | |||
7032 | /** |
||
7033 | * Creates/updates/deletes FTS mapping. |
||
7034 | * |
||
7035 | * @param $ftscfg |
||
7036 | * @param array $mapping Array of tokens' names |
||
7037 | * @param string $action What to do with the mapping: add, alter or drop |
||
7038 | * @param string $dictname Dictionary that will process tokens given or null in case of drop action |
||
7039 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
7040 | * @internal param string $cfgname The name of the FTS configuration to alter |
||
7041 | */ |
||
7042 | public function changeFtsMapping($ftscfg, $mapping, $action, $dictname = null) |
||
7043 | { |
||
7044 | if (count($mapping) > 0) { |
||
7045 | $f_schema = $this->_schema; |
||
7046 | $this->fieldClean($f_schema); |
||
7047 | $this->fieldClean($ftscfg); |
||
7048 | $this->fieldClean($dictname); |
||
7049 | $this->arrayClean($mapping); |
||
7050 | |||
7051 | switch ($action) { |
||
7052 | case 'alter': |
||
7053 | $whatToDo = 'ALTER'; |
||
7054 | break; |
||
7055 | case 'drop': |
||
7056 | $whatToDo = 'DROP'; |
||
7057 | break; |
||
7058 | default: |
||
7059 | $whatToDo = 'ADD'; |
||
7060 | break; |
||
7061 | } |
||
7062 | |||
7063 | $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\" {$whatToDo} MAPPING FOR "; |
||
7064 | $sql .= implode(',', $mapping); |
||
7065 | if ($action != 'drop' && !empty($dictname)) { |
||
7066 | $sql .= " WITH {$dictname}"; |
||
7067 | } |
||
7068 | |||
7069 | return $this->execute($sql); |
||
7070 | } |
||
7071 | |||
7072 | return -1; |
||
7073 | } |
||
7074 | |||
7075 | /** |
||
7076 | * Return all information related to a given FTS configuration's mapping |
||
7077 | * |
||
7078 | * @param $ftscfg The name of the FTS configuration |
||
7079 | * @param $mapping The name of the mapping |
||
7080 | * |
||
7081 | * @return FTS configuration information |
||
7082 | */ |
||
7083 | public function getFtsMappingByName($ftscfg, $mapping) |
||
7084 | { |
||
7085 | $c_schema = $this->_schema; |
||
7086 | $this->clean($c_schema); |
||
7087 | $this->clean($ftscfg); |
||
7088 | $this->clean($mapping); |
||
7089 | |||
7090 | $oidSet = $this->selectSet("SELECT c.oid, cfgparser |
||
7091 | FROM pg_catalog.pg_ts_config AS c |
||
7092 | LEFT JOIN pg_catalog.pg_namespace AS n ON n.oid = c.cfgnamespace |
||
7093 | WHERE c.cfgname = '{$ftscfg}' |
||
7094 | AND n.nspname='{$c_schema}'"); |
||
7095 | |||
7096 | $oid = $oidSet->fields['oid']; |
||
7097 | $cfgparser = $oidSet->fields['cfgparser']; |
||
7098 | |||
7099 | $tokenIdSet = $this->selectSet("SELECT tokid |
||
7100 | FROM pg_catalog.ts_token_type({$cfgparser}) |
||
7101 | WHERE alias = '{$mapping}'"); |
||
7102 | |||
7103 | $tokid = $tokenIdSet->fields['tokid']; |
||
7104 | |||
7105 | $sql = "SELECT |
||
7106 | (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name, |
||
7107 | d.dictname as dictionaries |
||
7108 | FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d |
||
7109 | WHERE c.oid = {$oid} AND m.mapcfg = c.oid AND m.maptokentype = {$tokid} AND m.mapdict = d.oid |
||
7110 | LIMIT 1;"; |
||
7111 | |||
7112 | return $this->selectSet($sql); |
||
7113 | } |
||
7114 | |||
7115 | /** |
||
7116 | * Return list of FTS mappings possible for given parser |
||
7117 | * (specified by given configuration since configuration can only have 1 parser) |
||
7118 | * |
||
7119 | * @param $ftscfg The config's name that use the parser |
||
7120 | * @return \PHPPgAdmin\Database\A 0 on success |
||
7121 | */ |
||
7122 | public function getFtsMappings($ftscfg) |
||
7123 | { |
||
7124 | $cfg = $this->getFtsConfigurationByName($ftscfg); |
||
7125 | |||
7126 | $sql = "SELECT alias AS name, description |
||
7127 | FROM pg_catalog.ts_token_type({$cfg->fields['parser_id']}) |
||
7128 | ORDER BY name"; |
||
7129 | |||
7130 | return $this->selectSet($sql); |
||
7131 | } |
||
7132 | |||
7133 | /** |
||
7134 | * Return all information related to a FTS configuration |
||
7135 | * |
||
7136 | * @param $ftscfg The name of the FTS configuration |
||
7137 | * |
||
7138 | * @return FTS configuration information |
||
7139 | */ |
||
7140 | public function getFtsConfigurationByName($ftscfg) |
||
7141 | { |
||
7142 | $c_schema = $this->_schema; |
||
7143 | $this->clean($c_schema); |
||
7144 | $this->clean($ftscfg); |
||
7145 | $sql = " |
||
7146 | SELECT |
||
7147 | n.nspname as schema, |
||
7148 | c.cfgname as name, |
||
7149 | p.prsname as parser, |
||
7150 | c.cfgparser as parser_id, |
||
7151 | pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment |
||
7152 | FROM pg_catalog.pg_ts_config c |
||
7153 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace |
||
7154 | LEFT JOIN pg_catalog.pg_ts_parser p ON p.oid = c.cfgparser |
||
7155 | WHERE pg_catalog.pg_ts_config_is_visible(c.oid) |
||
7156 | AND c.cfgname = '{$ftscfg}' |
||
7157 | AND n.nspname='{$c_schema}'"; |
||
7158 | |||
7159 | return $this->selectSet($sql); |
||
7160 | } |
||
7161 | |||
7162 | /** |
||
7163 | * Gets all languages |
||
7164 | * |
||
7165 | * @param bool|True $all True to get all languages, regardless of show_system |
||
7166 | * @return \PHPPgAdmin\Database\A recordset |
||
7167 | */ |
||
7168 | View Code Duplication | public function getLanguages($all = false) |
|
7169 | { |
||
7170 | $conf = $this->conf; |
||
7171 | |||
7172 | if ($conf['show_system'] || $all) { |
||
7173 | $where = ''; |
||
7174 | } else { |
||
7175 | $where = 'WHERE lanispl'; |
||
7176 | } |
||
7177 | |||
7178 | $sql = " |
||
7179 | SELECT |
||
7180 | lanname, lanpltrusted, |
||
7181 | lanplcallfoid::pg_catalog.regproc AS lanplcallf |
||
7182 | FROM |
||
7183 | pg_catalog.pg_language |
||
7184 | {$where} |
||
7185 | ORDER BY lanname |
||
7186 | "; |
||
7187 | |||
7188 | return $this->selectSet($sql); |
||
7189 | } |
||
7190 | |||
7191 | /** |
||
7192 | * Creates a new aggregate in the database |
||
7193 | * |
||
7194 | * @param $name The name of the aggregate |
||
7195 | * @param $basetype The input data type of the aggregate |
||
7196 | * @param $sfunc The name of the state transition function for the aggregate |
||
7197 | * @param $stype The data type for the aggregate's state value |
||
7198 | * @param $ffunc The name of the final function for the aggregate |
||
7199 | * @param $initcond The initial setting for the state value |
||
7200 | * @param $sortop The sort operator for the aggregate |
||
7201 | * @param $comment Aggregate comment |
||
7202 | * @return bool|int 0 success |
||
7203 | */ |
||
7204 | public function createAggregate($name, $basetype, $sfunc, $stype, $ffunc, $initcond, $sortop, $comment) |
||
7205 | { |
||
7206 | $f_schema = $this->_schema; |
||
7207 | $this->fieldClean($f_schema); |
||
7208 | $this->fieldClean($name); |
||
7209 | $this->fieldClean($basetype); |
||
7210 | $this->fieldClean($sfunc); |
||
7211 | $this->fieldClean($stype); |
||
7212 | $this->fieldClean($ffunc); |
||
7213 | $this->fieldClean($initcond); |
||
7214 | $this->fieldClean($sortop); |
||
7215 | |||
7216 | $this->beginTransaction(); |
||
7217 | |||
7218 | $sql = "CREATE AGGREGATE \"{$f_schema}\".\"{$name}\" (BASETYPE = \"{$basetype}\", SFUNC = \"{$sfunc}\", STYPE = \"{$stype}\""; |
||
7219 | if (trim($ffunc) != '') { |
||
7220 | $sql .= ", FINALFUNC = \"{$ffunc}\""; |
||
7221 | } |
||
7222 | |||
7223 | if (trim($initcond) != '') { |
||
7224 | $sql .= ", INITCOND = \"{$initcond}\""; |
||
7225 | } |
||
7226 | |||
7227 | if (trim($sortop) != '') { |
||
7228 | $sql .= ", SORTOP = \"{$sortop}\""; |
||
7229 | } |
||
7230 | |||
7231 | $sql .= ')'; |
||
7232 | |||
7233 | $status = $this->execute($sql); |
||
7234 | if ($status) { |
||
7235 | $this->rollbackTransaction(); |
||
7236 | |||
7237 | return -1; |
||
7238 | } |
||
7239 | |||
7240 | View Code Duplication | if (trim($comment) != '') { |
|
7241 | $status = $this->setComment('AGGREGATE', $name, '', $comment, $basetype); |
||
7242 | if ($status) { |
||
7243 | $this->rollbackTransaction(); |
||
7244 | |||
7245 | return -1; |
||
7246 | } |
||
7247 | } |
||
7248 | |||
7249 | return $this->endTransaction(); |
||
7250 | } |
||
7251 | |||
7252 | /** |
||
7253 | * Removes an aggregate function from the database |
||
7254 | * |
||
7255 | * @param $aggrname The name of the aggregate |
||
7256 | * @param $aggrtype The input data type of the aggregate |
||
7257 | * @param $cascade True to cascade drop, false to restrict |
||
7258 | * @return \PHPPgAdmin\Database\A 0 success |
||
7259 | */ |
||
7260 | View Code Duplication | public function dropAggregate($aggrname, $aggrtype, $cascade) |
|
7261 | { |
||
7262 | $f_schema = $this->_schema; |
||
7263 | $this->fieldClean($f_schema); |
||
7264 | $this->fieldClean($aggrname); |
||
7265 | $this->fieldClean($aggrtype); |
||
7266 | |||
7267 | $sql = "DROP AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\")"; |
||
7268 | if ($cascade) { |
||
7269 | $sql .= ' CASCADE'; |
||
7270 | } |
||
7271 | |||
7272 | return $this->execute($sql); |
||
7273 | } |
||
7274 | |||
7275 | /** |
||
7276 | * Gets all information for an aggregate |
||
7277 | * |
||
7278 | * @param $name The name of the aggregate |
||
7279 | * @param $basetype The input data type of the aggregate |
||
7280 | * @return A recordset |
||
7281 | */ |
||
7282 | public function getAggregate($name, $basetype) |
||
7283 | { |
||
7284 | $c_schema = $this->_schema; |
||
7285 | $this->clean($c_schema); |
||
7286 | $this->fieldClean($name); |
||
7287 | $this->fieldClean($basetype); |
||
7288 | |||
7289 | $sql = " |
||
7290 | SELECT p.proname, CASE p.proargtypes[0] |
||
7291 | WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL |
||
7292 | ELSE pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes, |
||
7293 | a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn, |
||
7294 | a.agginitval, a.aggsortop, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment |
||
7295 | FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a |
||
7296 | WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid |
||
7297 | AND p.proisagg AND n.nspname='{$c_schema}' |
||
7298 | AND p.proname='" . $name . "' |
||
7299 | AND CASE p.proargtypes[0] |
||
7300 | WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN '' |
||
7301 | ELSE pg_catalog.format_type(p.proargtypes[0], NULL) |
||
7302 | END ='" . $basetype . "'"; |
||
7303 | |||
7304 | return $this->selectSet($sql); |
||
7305 | } |
||
7306 | |||
7307 | /** |
||
7308 | * Gets all aggregates |
||
7309 | * |
||
7310 | * @return A recordset |
||
7311 | */ |
||
7312 | public function getAggregates() |
||
7313 | { |
||
7314 | $c_schema = $this->_schema; |
||
7315 | $this->clean($c_schema); |
||
7316 | $sql = "SELECT p.proname, CASE p.proargtypes[0] WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL ELSE |
||
7317 | pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes, a.aggtransfn, u.usename, |
||
7318 | pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment |
||
7319 | FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a |
||
7320 | WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid |
||
7321 | AND p.proisagg AND n.nspname='{$c_schema}' ORDER BY 1, 2"; |
||
7322 | |||
7323 | return $this->selectSet($sql); |
||
7324 | } |
||
7325 | |||
7326 | /** |
||
7327 | * Alters an aggregate |
||
7328 | * |
||
7329 | * @param $aggrname The actual name of the aggregate |
||
7330 | * @param $aggrtype The actual input data type of the aggregate |
||
7331 | * @param $aggrowner The actual owner of the aggregate |
||
7332 | * @param $aggrschema The actual schema the aggregate belongs to |
||
7333 | * @param $aggrcomment The actual comment for the aggregate |
||
7334 | * @param $newaggrname The new name of the aggregate |
||
7335 | * @param $newaggrowner The new owner of the aggregate |
||
7336 | * @param $newaggrschema The new schema where the aggregate will belong to |
||
7337 | * @param $newaggrcomment The new comment for the aggregate |
||
7338 | * @return bool|int 0 success |
||
7339 | */ |
||
7340 | public function alterAggregate( |
||
7341 | $aggrname, |
||
7342 | $aggrtype, |
||
7343 | $aggrowner, |
||
7344 | $aggrschema, |
||
7345 | $aggrcomment, |
||
7346 | $newaggrname, |
||
7347 | $newaggrowner, |
||
7348 | $newaggrschema, |
||
7349 | $newaggrcomment |
||
7350 | ) { |
||
7351 | // Clean fields |
||
7352 | $this->fieldClean($aggrname); |
||
7353 | $this->fieldClean($aggrtype); |
||
7354 | $this->fieldClean($aggrowner); |
||
7355 | $this->fieldClean($aggrschema); |
||
7356 | $this->fieldClean($newaggrname); |
||
7357 | $this->fieldClean($newaggrowner); |
||
7358 | $this->fieldClean($newaggrschema); |
||
7359 | |||
7360 | $this->beginTransaction(); |
||
7361 | |||
7362 | // Change the owner, if it has changed |
||
7363 | if ($aggrowner != $newaggrowner) { |
||
7364 | $status = $this->changeAggregateOwner($aggrname, $aggrtype, $newaggrowner); |
||
7365 | if ($status != 0) { |
||
7366 | $this->rollbackTransaction(); |
||
7367 | |||
7368 | return -1; |
||
7369 | } |
||
7370 | } |
||
7371 | |||
7372 | // Set the comment, if it has changed |
||
7373 | if ($aggrcomment != $newaggrcomment) { |
||
7374 | $status = $this->setComment('AGGREGATE', $aggrname, '', $newaggrcomment, $aggrtype); |
||
7375 | if ($status) { |
||
7376 | $this->rollbackTransaction(); |
||
7377 | |||
7378 | return -2; |
||
7379 | } |
||
7380 | } |
||
7381 | |||
7382 | // Change the schema, if it has changed |
||
7383 | if ($aggrschema != $newaggrschema) { |
||
7384 | $status = $this->changeAggregateSchema($aggrname, $aggrtype, $newaggrschema); |
||
7385 | if ($status != 0) { |
||
7386 | $this->rollbackTransaction(); |
||
7387 | |||
7388 | return -3; |
||
7389 | } |
||
7390 | } |
||
7391 | |||
7392 | // Rename the aggregate, if it has changed |
||
7393 | if ($aggrname != $newaggrname) { |
||
7394 | $status = $this->renameAggregate($newaggrschema, $aggrname, $aggrtype, $newaggrname); |
||
7395 | if ($status != 0) { |
||
7396 | $this->rollbackTransaction(); |
||
7397 | |||
7398 | return -4; |
||
7399 | } |
||
7400 | } |
||
7401 | |||
7402 | return $this->endTransaction(); |
||
7403 | } |
||
7404 | |||
7405 | /** |
||
7406 | * Changes the owner of an aggregate function |
||
7407 | * |
||
7408 | * @param $aggrname The name of the aggregate |
||
7409 | * @param $aggrtype The input data type of the aggregate |
||
7410 | * @param $newaggrowner The new owner of the aggregate |
||
7411 | * @return \PHPPgAdmin\Database\A 0 success |
||
7412 | */ |
||
7413 | View Code Duplication | public function changeAggregateOwner($aggrname, $aggrtype, $newaggrowner) |
|
7414 | { |
||
7415 | $f_schema = $this->_schema; |
||
7416 | $this->fieldClean($f_schema); |
||
7417 | $this->fieldClean($aggrname); |
||
7418 | $this->fieldClean($newaggrowner); |
||
7419 | $sql = "ALTER AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") OWNER TO \"{$newaggrowner}\""; |
||
7420 | |||
7421 | return $this->execute($sql); |
||
7422 | } |
||
7423 | |||
7424 | /** |
||
7425 | * Changes the schema of an aggregate function |
||
7426 | * |
||
7427 | * @param $aggrname The name of the aggregate |
||
7428 | * @param $aggrtype The input data type of the aggregate |
||
7429 | * @param $newaggrschema The new schema for the aggregate |
||
7430 | * @return \PHPPgAdmin\Database\A 0 success |
||
7431 | */ |
||
7432 | View Code Duplication | public function changeAggregateSchema($aggrname, $aggrtype, $newaggrschema) |
|
7433 | { |
||
7434 | $f_schema = $this->_schema; |
||
7435 | $this->fieldClean($f_schema); |
||
7436 | $this->fieldClean($aggrname); |
||
7437 | $this->fieldClean($newaggrschema); |
||
7438 | $sql = "ALTER AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") SET SCHEMA \"{$newaggrschema}\""; |
||
7439 | |||
7440 | return $this->execute($sql); |
||
7441 | } |
||
7442 | |||
7443 | /** |
||
7444 | * Renames an aggregate function |
||
7445 | * |
||
7446 | * @param $aggrschema |
||
7447 | * @param $aggrname The actual name of the aggregate |
||
7448 | * @param $aggrtype The actual input data type of the aggregate |
||
7449 | * @param $newaggrname The new name of the aggregate |
||
7450 | * @return \PHPPgAdmin\Database\A 0 success |
||
7451 | */ |
||
7452 | public function renameAggregate($aggrschema, $aggrname, $aggrtype, $newaggrname) |
||
7453 | { |
||
7454 | /* this function is called from alterAggregate where params are cleaned */ |
||
7455 | $sql = "ALTER AGGREGATE \"{$aggrschema}\"" . '.' . "\"{$aggrname}\" (\"{$aggrtype}\") RENAME TO \"{$newaggrname}\""; |
||
7456 | |||
7457 | return $this->execute($sql); |
||
7458 | } |
||
7459 | |||
7460 | /** |
||
7461 | * Returns all roles in the database cluster |
||
7462 | * |
||
7463 | * @param $rolename (optional) The role name to exclude from the select |
||
7464 | * @return All roles |
||
7465 | */ |
||
7466 | public function getRoles($rolename = '') |
||
7467 | { |
||
7468 | $sql = ' |
||
7469 | SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit, |
||
7470 | rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig |
||
7471 | FROM pg_catalog.pg_roles'; |
||
7472 | if ($rolename) { |
||
7473 | $sql .= " WHERE rolname!='{$rolename}'"; |
||
7474 | } |
||
7475 | |||
7476 | $sql .= ' ORDER BY rolname'; |
||
7477 | |||
7478 | return $this->selectSet($sql); |
||
7479 | } |
||
7480 | |||
7481 | /** |
||
7482 | * Returns information about a single role |
||
7483 | * |
||
7484 | * @param $rolename The name of the role to retrieve |
||
7485 | * @return The role's data |
||
7486 | */ |
||
7487 | public function getRole($rolename) |
||
7488 | { |
||
7489 | $this->clean($rolename); |
||
7490 | |||
7491 | $sql = " |
||
7492 | SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit, |
||
7493 | rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig |
||
7494 | FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'"; |
||
7495 | |||
7496 | return $this->selectSet($sql); |
||
7497 | } |
||
7498 | |||
7499 | /** |
||
7500 | * Returns all users in the database cluster |
||
7501 | * |
||
7502 | * @return All users |
||
7503 | */ |
||
7504 | public function getUsers() |
||
7505 | { |
||
7506 | $sql = 'SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig |
||
7507 | FROM pg_user |
||
7508 | ORDER BY usename'; |
||
7509 | |||
7510 | return $this->selectSet($sql); |
||
7511 | } |
||
7512 | |||
7513 | /** |
||
7514 | * Returns information about a single user |
||
7515 | * |
||
7516 | * @param $username The username of the user to retrieve |
||
7517 | * @return The user's data |
||
7518 | */ |
||
7519 | public function getUser($username) |
||
7520 | { |
||
7521 | $this->clean($username); |
||
7522 | |||
7523 | $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig |
||
7524 | FROM pg_user |
||
7525 | WHERE usename='{$username}'"; |
||
7526 | |||
7527 | return $this->selectSet($sql); |
||
7528 | } |
||
7529 | |||
7530 | /** |
||
7531 | * Creates a new role |
||
7532 | * |
||
7533 | * @param $rolename The name of the role to create |
||
7534 | * @param $password A password for the role |
||
7535 | * @param $superuser Boolean whether or not the role is a superuser |
||
7536 | * @param $createdb Boolean whether or not the role can create databases |
||
7537 | * @param $createrole Boolean whether or not the role can create other roles |
||
7538 | * @param $inherits Boolean whether or not the role inherits the privileges from parent roles |
||
7539 | * @param $login Boolean whether or not the role will be allowed to login |
||
7540 | * @param $connlimit Number of concurrent connections the role can make |
||
7541 | * @param $expiry String Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire |
||
7542 | * @param $memberof (array) Roles to which the new role will be immediately added as a new member |
||
7543 | * @param $members (array) Roles which are automatically added as members of the new role |
||
7544 | * @param $adminmembers (array) Roles which are automatically added as admin members of the new role |
||
7545 | * @return \PHPPgAdmin\Database\A 0 success |
||
7546 | */ |
||
7547 | public function createRole( |
||
7548 | $rolename, |
||
7549 | $password, |
||
7550 | $superuser, |
||
7551 | $createdb, |
||
7552 | $createrole, |
||
7553 | $inherits, |
||
7554 | $login, |
||
7555 | $connlimit, |
||
7556 | $expiry, |
||
7557 | $memberof, |
||
7558 | $members, |
||
7559 | $adminmembers |
||
7560 | ) { |
||
7561 | $enc = $this->_encryptPassword($rolename, $password); |
||
7562 | $this->fieldClean($rolename); |
||
7563 | $this->clean($enc); |
||
7564 | $this->clean($connlimit); |
||
7565 | $this->clean($expiry); |
||
7566 | $this->fieldArrayClean($memberof); |
||
7567 | $this->fieldArrayClean($members); |
||
7568 | $this->fieldArrayClean($adminmembers); |
||
7569 | |||
7570 | $sql = "CREATE ROLE \"{$rolename}\""; |
||
7571 | if ($password != '') { |
||
7572 | $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'"; |
||
7573 | } |
||
7574 | |||
7575 | $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER'; |
||
7576 | $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB'; |
||
7577 | $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE'; |
||
7578 | $sql .= $inherits ? ' INHERIT' : ' NOINHERIT'; |
||
7579 | $sql .= $login ? ' LOGIN' : ' NOLOGIN'; |
||
7580 | if ($connlimit != '') { |
||
7581 | $sql .= " CONNECTION LIMIT {$connlimit}"; |
||
7582 | } else { |
||
7583 | $sql .= ' CONNECTION LIMIT -1'; |
||
7584 | } |
||
7585 | |||
7586 | if ($expiry != '') { |
||
7587 | $sql .= " VALID UNTIL '{$expiry}'"; |
||
7588 | } else { |
||
7589 | $sql .= " VALID UNTIL 'infinity'"; |
||
7590 | } |
||
7591 | |||
7592 | if (is_array($memberof) && sizeof($memberof) > 0) { |
||
7593 | $sql .= ' IN ROLE "' . join('", "', $memberof) . '"'; |
||
7594 | } |
||
7595 | |||
7596 | if (is_array($members) && sizeof($members) > 0) { |
||
7597 | $sql .= ' ROLE "' . join('", "', $members) . '"'; |
||
7598 | } |
||
7599 | |||
7600 | if (is_array($adminmembers) && sizeof($adminmembers) > 0) { |
||
7601 | $sql .= ' ADMIN "' . join('", "', $adminmembers) . '"'; |
||
7602 | } |
||
7603 | |||
7604 | return $this->execute($sql); |
||
7605 | } |
||
7606 | |||
7607 | /** |
||
7608 | * Helper function that computes encypted PostgreSQL passwords |
||
7609 | * |
||
7610 | * @param $username The username |
||
7611 | * @param $password The password |
||
7612 | * @return string |
||
7613 | */ |
||
7614 | public function _encryptPassword($username, $password) |
||
7615 | { |
||
7616 | return 'md5' . md5($password . $username); |
||
7617 | } |
||
7618 | |||
7619 | /** |
||
7620 | * Adjusts a role's info and renames it |
||
7621 | * |
||
7622 | * @param $rolename The name of the role to adjust |
||
7623 | * @param $password A password for the role |
||
7624 | * @param $superuser Boolean whether or not the role is a superuser |
||
7625 | * @param $createdb Boolean whether or not the role can create databases |
||
7626 | * @param $createrole Boolean whether or not the role can create other roles |
||
7627 | * @param $inherits Boolean whether or not the role inherits the privileges from parent roles |
||
7628 | * @param $login Boolean whether or not the role will be allowed to login |
||
7629 | * @param $connlimit Number of concurrent connections the role can make |
||
7630 | * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire |
||
7631 | * @param $memberof (array) Roles to which the role will be immediately added as a new member |
||
7632 | * @param $members (array) Roles which are automatically added as members of the role |
||
7633 | * @param $adminmembers (array) Roles which are automatically added as admin members of the role |
||
7634 | * @param $memberofold (array) Original roles whose the role belongs to |
||
7635 | * @param $membersold (array) Original roles that are members of the role |
||
7636 | * @param $adminmembersold (array) Original roles that are admin members of the role |
||
7637 | * @param $newrolename The new name of the role |
||
7638 | * @return bool|int 0 success |
||
7639 | */ |
||
7640 | public function setRenameRole( |
||
7641 | $rolename, |
||
7642 | $password, |
||
7643 | $superuser, |
||
7644 | $createdb, |
||
7645 | $createrole, |
||
7646 | $inherits, |
||
7647 | $login, |
||
7648 | $connlimit, |
||
7649 | $expiry, |
||
7650 | $memberof, |
||
7651 | $members, |
||
7652 | $adminmembers, |
||
7653 | $memberofold, |
||
7654 | $membersold, |
||
7655 | $adminmembersold, |
||
7656 | $newrolename |
||
7657 | ) { |
||
7658 | $status = $this->beginTransaction(); |
||
7659 | if ($status != 0) { |
||
7660 | return -1; |
||
7661 | } |
||
7662 | |||
7663 | if ($rolename != $newrolename) { |
||
7664 | $status = $this->renameRole($rolename, $newrolename); |
||
7665 | if ($status != 0) { |
||
7666 | $this->rollbackTransaction(); |
||
7667 | |||
7668 | return -3; |
||
7669 | } |
||
7670 | $rolename = $newrolename; |
||
7671 | } |
||
7672 | |||
7673 | $status = |
||
7674 | $this->setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, |
||
7675 | $adminmembers, $memberofold, $membersold, $adminmembersold); |
||
7676 | if ($status != 0) { |
||
7677 | $this->rollbackTransaction(); |
||
7678 | |||
7679 | return -2; |
||
7680 | } |
||
7681 | |||
7682 | return $this->endTransaction(); |
||
7683 | } |
||
7684 | |||
7685 | /** |
||
7686 | * Renames a role |
||
7687 | * |
||
7688 | * @param $rolename The name of the role to rename |
||
7689 | * @param $newrolename The new name of the role |
||
7690 | * @return \PHPPgAdmin\Database\A 0 success |
||
7691 | */ |
||
7692 | public function renameRole($rolename, $newrolename) |
||
7693 | { |
||
7694 | $this->fieldClean($rolename); |
||
7695 | $this->fieldClean($newrolename); |
||
7696 | |||
7697 | $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\""; |
||
7698 | |||
7699 | return $this->execute($sql); |
||
7700 | } |
||
7701 | |||
7702 | /** |
||
7703 | * Adjusts a role's info |
||
7704 | * |
||
7705 | * @param $rolename The name of the role to adjust |
||
7706 | * @param $password A password for the role |
||
7707 | * @param $superuser Boolean whether or not the role is a superuser |
||
7708 | * @param $createdb Boolean whether or not the role can create databases |
||
7709 | * @param $createrole Boolean whether or not the role can create other roles |
||
7710 | * @param $inherits Boolean whether or not the role inherits the privileges from parent roles |
||
7711 | * @param $login Boolean whether or not the role will be allowed to login |
||
7712 | * @param $connlimit Number of concurrent connections the role can make |
||
7713 | * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire |
||
7714 | * @param $memberof (array) Roles to which the role will be immediately added as a new member |
||
7715 | * @param $members (array) Roles which are automatically added as members of the role |
||
7716 | * @param $adminmembers (array) Roles which are automatically added as admin members of the role |
||
7717 | * @param $memberofold (array) Original roles whose the role belongs to |
||
7718 | * @param $membersold (array) Original roles that are members of the role |
||
7719 | * @param $adminmembersold (array) Original roles that are admin members of the role |
||
7720 | * @return int|\PHPPgAdmin\Database\A 0 success |
||
7721 | */ |
||
7722 | public function setRole( |
||
7723 | $rolename, |
||
7724 | $password, |
||
7725 | $superuser, |
||
7726 | $createdb, |
||
7727 | $createrole, |
||
7728 | $inherits, |
||
7729 | $login, |
||
7730 | $connlimit, |
||
7731 | $expiry, |
||
7732 | $memberof, |
||
7733 | $members, |
||
7734 | $adminmembers, |
||
7735 | $memberofold, |
||
7736 | $membersold, |
||
7737 | $adminmembersold |
||
7738 | ) { |
||
7739 | $enc = $this->_encryptPassword($rolename, $password); |
||
7740 | $this->fieldClean($rolename); |
||
7741 | $this->clean($enc); |
||
7742 | $this->clean($connlimit); |
||
7743 | $this->clean($expiry); |
||
7744 | $this->fieldArrayClean($memberof); |
||
7745 | $this->fieldArrayClean($members); |
||
7746 | $this->fieldArrayClean($adminmembers); |
||
7747 | |||
7748 | $sql = "ALTER ROLE \"{$rolename}\""; |
||
7749 | if ($password != '') { |
||
7750 | $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'"; |
||
7751 | } |
||
7752 | |||
7753 | $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER'; |
||
7754 | $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB'; |
||
7755 | $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE'; |
||
7756 | $sql .= $inherits ? ' INHERIT' : ' NOINHERIT'; |
||
7757 | $sql .= $login ? ' LOGIN' : ' NOLOGIN'; |
||
7758 | if ($connlimit != '') { |
||
7759 | $sql .= " CONNECTION LIMIT {$connlimit}"; |
||
7760 | } else { |
||
7761 | $sql .= ' CONNECTION LIMIT -1'; |
||
7762 | } |
||
7763 | |||
7764 | if ($expiry != '') { |
||
7765 | $sql .= " VALID UNTIL '{$expiry}'"; |
||
7766 | } else { |
||
7767 | $sql .= " VALID UNTIL 'infinity'"; |
||
7768 | } |
||
7769 | |||
7770 | $status = $this->execute($sql); |
||
7771 | |||
7772 | if ($status != 0) { |
||
7773 | return -1; |
||
7774 | } |
||
7775 | |||
7776 | //memberof |
||
7777 | $old = explode(',', $memberofold); |
||
7778 | foreach ($memberof as $m) { |
||
7779 | if (!in_array($m, $old)) { |
||
7780 | $status = $this->grantRole($m, $rolename); |
||
7781 | if ($status != 0) { |
||
7782 | return -1; |
||
7783 | } |
||
7784 | } |
||
7785 | } |
||
7786 | View Code Duplication | if ($memberofold) { |
|
7787 | foreach ($old as $o) { |
||
7788 | if (!in_array($o, $memberof)) { |
||
7789 | $status = $this->revokeRole($o, $rolename, 0, 'CASCADE'); |
||
7790 | if ($status != 0) { |
||
7791 | return -1; |
||
7792 | } |
||
7793 | } |
||
7794 | } |
||
7795 | } |
||
7796 | |||
7797 | //members |
||
7798 | $old = explode(',', $membersold); |
||
7799 | foreach ($members as $m) { |
||
7800 | if (!in_array($m, $old)) { |
||
7801 | $status = $this->grantRole($rolename, $m); |
||
7802 | if ($status != 0) { |
||
7803 | return -1; |
||
7804 | } |
||
7805 | } |
||
7806 | } |
||
7807 | View Code Duplication | if ($membersold) { |
|
7808 | foreach ($old as $o) { |
||
7809 | if (!in_array($o, $members)) { |
||
7810 | $status = $this->revokeRole($rolename, $o, 0, 'CASCADE'); |
||
7811 | if ($status != 0) { |
||
7812 | return -1; |
||
7813 | } |
||
7814 | } |
||
7815 | } |
||
7816 | } |
||
7817 | |||
7818 | //adminmembers |
||
7819 | $old = explode(',', $adminmembersold); |
||
7820 | foreach ($adminmembers as $m) { |
||
7821 | if (!in_array($m, $old)) { |
||
7822 | $status = $this->grantRole($rolename, $m, 1); |
||
7823 | if ($status != 0) { |
||
7824 | return -1; |
||
7825 | } |
||
7826 | } |
||
7827 | } |
||
7828 | View Code Duplication | if ($adminmembersold) { |
|
7829 | foreach ($old as $o) { |
||
7830 | if (!in_array($o, $adminmembers)) { |
||
7831 | $status = $this->revokeRole($rolename, $o, 1, 'CASCADE'); |
||
7832 | if ($status != 0) { |
||
7833 | return -1; |
||
7834 | } |
||
7835 | } |
||
7836 | } |
||
7837 | } |
||
7838 | |||
7839 | return $status; |
||
7840 | } |
||
7841 | |||
7842 | /** |
||
7843 | * Grants membership in a role |
||
7844 | * |
||
7845 | * @param $role The name of the target role |
||
7846 | * @param $rolename The name of the role that will belong to the target role |
||
7847 | * @param int $admin (optional) Flag to grant the admin option |
||
7848 | * @return \PHPPgAdmin\Database\A 0 success |
||
7849 | */ |
||
7850 | View Code Duplication | public function grantRole($role, $rolename, $admin = 0) |
|
7851 | { |
||
7852 | $this->fieldClean($role); |
||
7853 | $this->fieldClean($rolename); |
||
7854 | |||
7855 | $sql = "GRANT \"{$role}\" TO \"{$rolename}\""; |
||
7856 | if ($admin == 1) { |
||
7857 | $sql .= ' WITH ADMIN OPTION'; |
||
7858 | } |
||
7859 | |||
7860 | return $this->execute($sql); |
||
7861 | } |
||
7862 | |||
7863 | /** |
||
7864 | * Revokes membership in a role |
||
7865 | * |
||
7866 | * @param $role The name of the target role |
||
7867 | * @param $rolename The name of the role that will not belong to the target role |
||
7868 | * @param int $admin (optional) Flag to revoke only the admin option |
||
7869 | * @param string $type (optional) Type of revoke: RESTRICT | CASCADE |
||
7870 | * @return \PHPPgAdmin\Database\A 0 success |
||
7871 | */ |
||
7872 | View Code Duplication | public function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT') |
|
7873 | { |
||
7874 | $this->fieldClean($role); |
||
7875 | $this->fieldClean($rolename); |
||
7876 | |||
7877 | $sql = 'REVOKE '; |
||
7878 | if ($admin == 1) { |
||
7879 | $sql .= 'ADMIN OPTION FOR '; |
||
7880 | } |
||
7881 | |||
7882 | $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}"; |
||
7883 | |||
7884 | return $this->execute($sql); |
||
7885 | } |
||
7886 | |||
7887 | /** |
||
7888 | * Removes a role |
||
7889 | * |
||
7890 | * @param $rolename The name of the role to drop |
||
7891 | * @return \PHPPgAdmin\Database\A 0 success |
||
7892 | */ |
||
7893 | public function dropRole($rolename) |
||
7894 | { |
||
7895 | $this->fieldClean($rolename); |
||
7896 | |||
7897 | $sql = "DROP ROLE \"{$rolename}\""; |
||
7898 | |||
7899 | return $this->execute($sql); |
||
7900 | } |
||
7901 | |||
7902 | /** |
||
7903 | * Creates a new user |
||
7904 | * |
||
7905 | * @param $username The username of the user to create |
||
7906 | * @param $password A password for the user |
||
7907 | * @param $createdb boolean Whether or not the user can create databases |
||
7908 | * @param $createuser boolean Whether or not the user can create other users |
||
7909 | * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire |
||
7910 | * @param $groups |
||
7911 | * @return \PHPPgAdmin\Database\A 0 success |
||
7912 | * @internal param $group (array) The groups to create the user in |
||
7913 | */ |
||
7914 | public function createUser($username, $password, $createdb, $createuser, $expiry, $groups) |
||
7915 | { |
||
7916 | $enc = $this->_encryptPassword($username, $password); |
||
7917 | $this->fieldClean($username); |
||
7918 | $this->clean($enc); |
||
7919 | $this->clean($expiry); |
||
7920 | $this->fieldArrayClean($groups); |
||
7921 | |||
7922 | $sql = "CREATE USER \"{$username}\""; |
||
7923 | if ($password != '') { |
||
7924 | $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'"; |
||
7925 | } |
||
7926 | |||
7927 | $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB'; |
||
7928 | $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER'; |
||
7929 | if (is_array($groups) && sizeof($groups) > 0) { |
||
7930 | $sql .= ' IN GROUP "' . join('", "', $groups) . '"'; |
||
7931 | } |
||
7932 | |||
7933 | if ($expiry != '') { |
||
7934 | $sql .= " VALID UNTIL '{$expiry}'"; |
||
7935 | } else { |
||
7936 | $sql .= " VALID UNTIL 'infinity'"; |
||
7937 | } |
||
7938 | |||
7939 | return $this->execute($sql); |
||
7940 | } |
||
7941 | |||
7942 | /** |
||
7943 | * Adjusts a user's info and renames the user |
||
7944 | * |
||
7945 | * @param $username The username of the user to modify |
||
7946 | * @param $password A new password for the user |
||
7947 | * @param $createdb boolean Whether or not the user can create databases |
||
7948 | * @param $createuser boolean Whether or not the user can create other users |
||
7949 | * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire. |
||
7950 | * @param $newname The new name of the user |
||
7951 | * @return bool|int 0 success |
||
7952 | */ |
||
7953 | public function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname) |
||
7954 | { |
||
7955 | $status = $this->beginTransaction(); |
||
7956 | if ($status != 0) { |
||
7957 | return -1; |
||
7958 | } |
||
7959 | |||
7960 | if ($username != $newname) { |
||
7961 | $status = $this->renameUser($username, $newname); |
||
7962 | if ($status != 0) { |
||
7963 | $this->rollbackTransaction(); |
||
7964 | |||
7965 | return -3; |
||
7966 | } |
||
7967 | $username = $newname; |
||
7968 | } |
||
7969 | |||
7970 | $status = $this->setUser($username, $password, $createdb, $createuser, $expiry); |
||
7971 | if ($status != 0) { |
||
7972 | $this->rollbackTransaction(); |
||
7973 | |||
7974 | return -2; |
||
7975 | } |
||
7976 | |||
7977 | return $this->endTransaction(); |
||
7978 | } |
||
7979 | |||
7980 | /** |
||
7981 | * Renames a user |
||
7982 | * |
||
7983 | * @param $username The username of the user to rename |
||
7984 | * @param $newname The new name of the user |
||
7985 | * @return \PHPPgAdmin\Database\A 0 success |
||
7986 | */ |
||
7987 | public function renameUser($username, $newname) |
||
7988 | { |
||
7989 | $this->fieldClean($username); |
||
7990 | $this->fieldClean($newname); |
||
7991 | |||
7992 | $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\""; |
||
7993 | |||
7994 | return $this->execute($sql); |
||
7995 | } |
||
7996 | |||
7997 | // Tablespace functions |
||
7998 | |||
7999 | /** |
||
8000 | * Adjusts a user's info |
||
8001 | * |
||
8002 | * @param $username The username of the user to modify |
||
8003 | * @param $password A new password for the user |
||
8004 | * @param $createdb boolean Whether or not the user can create databases |
||
8005 | * @param $createuser boolean Whether or not the user can create other users |
||
8006 | * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire. |
||
8007 | * @return \PHPPgAdmin\Database\A 0 success |
||
8008 | */ |
||
8009 | public function setUser($username, $password, $createdb, $createuser, $expiry) |
||
8010 | { |
||
8011 | $enc = $this->_encryptPassword($username, $password); |
||
8012 | $this->fieldClean($username); |
||
8013 | $this->clean($enc); |
||
8014 | $this->clean($expiry); |
||
8015 | |||
8016 | $sql = "ALTER USER \"{$username}\""; |
||
8017 | if ($password != '') { |
||
8018 | $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'"; |
||
8019 | } |
||
8020 | |||
8021 | $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB'; |
||
8022 | $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER'; |
||
8023 | if ($expiry != '') { |
||
8024 | $sql .= " VALID UNTIL '{$expiry}'"; |
||
8025 | } else { |
||
8026 | $sql .= " VALID UNTIL 'infinity'"; |
||
8027 | } |
||
8028 | |||
8029 | return $this->execute($sql); |
||
8030 | } |
||
8031 | |||
8032 | /** |
||
8033 | * Removes a user |
||
8034 | * |
||
8035 | * @param $username The username of the user to drop |
||
8036 | * @return \PHPPgAdmin\Database\A 0 success |
||
8037 | */ |
||
8038 | public function dropUser($username) |
||
8039 | { |
||
8040 | $this->fieldClean($username); |
||
8041 | |||
8042 | $sql = "DROP USER \"{$username}\""; |
||
8043 | |||
8044 | return $this->execute($sql); |
||
8045 | } |
||
8046 | |||
8047 | /** |
||
8048 | * Changes a role's password |
||
8049 | * |
||
8050 | * @param $rolename The role name |
||
8051 | * @param $password The new password |
||
8052 | * @return \PHPPgAdmin\Database\A 0 success |
||
8053 | */ |
||
8054 | View Code Duplication | public function changePassword($rolename, $password) |
|
8055 | { |
||
8056 | $enc = $this->_encryptPassword($rolename, $password); |
||
8057 | $this->fieldClean($rolename); |
||
8058 | $this->clean($enc); |
||
8059 | |||
8060 | $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'"; |
||
8061 | |||
8062 | return $this->execute($sql); |
||
8063 | } |
||
8064 | |||
8065 | /** |
||
8066 | * Adds a group member |
||
8067 | * |
||
8068 | * @param $groname The name of the group |
||
8069 | * @param $user The name of the user to add to the group |
||
8070 | * @return \PHPPgAdmin\Database\A 0 success |
||
8071 | */ |
||
8072 | public function addGroupMember($groname, $user) |
||
8073 | { |
||
8074 | $this->fieldClean($groname); |
||
8075 | $this->fieldClean($user); |
||
8076 | |||
8077 | $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\""; |
||
8078 | |||
8079 | return $this->execute($sql); |
||
8080 | } |
||
8081 | |||
8082 | /** |
||
8083 | * Returns all role names which the role belongs to |
||
8084 | * |
||
8085 | * @param $rolename The role name |
||
8086 | * @return All role names |
||
8087 | */ |
||
8088 | public function getMemberOf($rolename) |
||
8089 | { |
||
8090 | $this->clean($rolename); |
||
8091 | |||
8092 | $sql = " |
||
8093 | SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M |
||
8094 | WHERE R.oid=M.roleid |
||
8095 | AND member IN ( |
||
8096 | SELECT oid FROM pg_catalog.pg_roles |
||
8097 | WHERE rolname='{$rolename}') |
||
8098 | ORDER BY rolname"; |
||
8099 | |||
8100 | return $this->selectSet($sql); |
||
8101 | } |
||
8102 | |||
8103 | // Administration functions |
||
8104 | |||
8105 | /** |
||
8106 | * Returns all role names that are members of a role |
||
8107 | * |
||
8108 | * @param $rolename The role name |
||
8109 | * @param $admin (optional) Find only admin members |
||
8110 | * @return All role names |
||
8111 | */ |
||
8112 | public function getMembers($rolename, $admin = 'f') |
||
8113 | { |
||
8114 | $this->clean($rolename); |
||
8115 | |||
8116 | $sql = " |
||
8117 | SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M |
||
8118 | WHERE R.oid=M.member AND admin_option='{$admin}' |
||
8119 | AND roleid IN (SELECT oid FROM pg_catalog.pg_roles |
||
8120 | WHERE rolname='{$rolename}') |
||
8121 | ORDER BY rolname"; |
||
8122 | |||
8123 | return $this->selectSet($sql); |
||
8124 | } |
||
8125 | |||
8126 | /** |
||
8127 | * Removes a group member |
||
8128 | * |
||
8129 | * @param $groname The name of the group |
||
8130 | * @param $user The name of the user to remove from the group |
||
8131 | * @return \PHPPgAdmin\Database\A 0 success |
||
8132 | */ |
||
8133 | public function dropGroupMember($groname, $user) |
||
8134 | { |
||
8135 | $this->fieldClean($groname); |
||
8136 | $this->fieldClean($user); |
||
8137 | |||
8138 | $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\""; |
||
8139 | |||
8140 | return $this->execute($sql); |
||
8141 | } |
||
8142 | |||
8143 | /** |
||
8144 | * Return users in a specific group |
||
8145 | * |
||
8146 | * @param $groname The name of the group |
||
8147 | * @return All users in the group |
||
8148 | */ |
||
8149 | public function getGroup($groname) |
||
8150 | { |
||
8151 | $this->clean($groname); |
||
8152 | |||
8153 | $sql = " |
||
8154 | SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g |
||
8155 | WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist) |
||
8156 | ORDER BY s.usename"; |
||
8157 | |||
8158 | return $this->selectSet($sql); |
||
8159 | } |
||
8160 | |||
8161 | /** |
||
8162 | * Returns all groups in the database cluser |
||
8163 | * |
||
8164 | * @return All groups |
||
8165 | */ |
||
8166 | public function getGroups() |
||
8167 | { |
||
8168 | $sql = 'SELECT groname FROM pg_group ORDER BY groname'; |
||
8169 | |||
8170 | return $this->selectSet($sql); |
||
8171 | } |
||
8172 | |||
8173 | /** |
||
8174 | * Creates a new group |
||
8175 | * |
||
8176 | * @param $groname The name of the group |
||
8177 | * @param $users An array of users to add to the group |
||
8178 | * @return \PHPPgAdmin\Database\A 0 success |
||
8179 | */ |
||
8180 | public function createGroup($groname, $users) |
||
8181 | { |
||
8182 | $this->fieldClean($groname); |
||
8183 | |||
8184 | $sql = "CREATE GROUP \"{$groname}\""; |
||
8185 | |||
8186 | if (is_array($users) && sizeof($users) > 0) { |
||
8187 | $this->fieldArrayClean($users); |
||
8188 | $sql .= ' WITH USER "' . join('", "', $users) . '"'; |
||
8189 | } |
||
8190 | |||
8191 | return $this->execute($sql); |
||
8192 | } |
||
8193 | |||
8194 | /** |
||
8195 | * Removes a group |
||
8196 | * |
||
8197 | * @param $groname The name of the group to drop |
||
8198 | * @return \PHPPgAdmin\Database\A 0 success |
||
8199 | */ |
||
8200 | public function dropGroup($groname) |
||
8201 | { |
||
8202 | $this->fieldClean($groname); |
||
8203 | |||
8204 | $sql = "DROP GROUP \"{$groname}\""; |
||
8205 | |||
8206 | return $this->execute($sql); |
||
8207 | } |
||
8208 | |||
8209 | /** |
||
8210 | * Grants a privilege to a user, group or public |
||
8211 | * |
||
8212 | * @param $mode 'GRANT' or 'REVOKE'; |
||
8213 | * @param $type The type of object |
||
8214 | * @param $object The name of the object |
||
8215 | * @param $public True to grant to public, false otherwise |
||
8216 | * @param $usernames The array of usernames to grant privs to. |
||
8217 | * @param $groupnames The array of group names to grant privs to. |
||
8218 | * @param $privileges The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) ) |
||
8219 | * @param $grantoption True if has grant option, false otherwise |
||
8220 | * @param $cascade True for cascade revoke, false otherwise |
||
8221 | * @param $table the column's table if type=column |
||
8222 | * @return \PHPPgAdmin\Database\A 0 success |
||
8223 | */ |
||
8224 | public function setPrivileges( |
||
8225 | $mode, |
||
8226 | $type, |
||
8227 | $object, |
||
8228 | $public, |
||
8229 | $usernames, |
||
8230 | $groupnames, |
||
8231 | $privileges, |
||
8232 | $grantoption, |
||
8233 | $cascade, |
||
8234 | $table |
||
8235 | ) { |
||
8236 | $f_schema = $this->_schema; |
||
8237 | $this->fieldClean($f_schema); |
||
8238 | $this->fieldArrayClean($usernames); |
||
8239 | $this->fieldArrayClean($groupnames); |
||
8240 | |||
8241 | // Input checking |
||
8242 | if (!is_array($privileges) || sizeof($privileges) == 0) { |
||
8243 | return -3; |
||
8244 | } |
||
8245 | |||
8246 | if (!is_array($usernames) || !is_array($groupnames) || |
||
8247 | (!$public && sizeof($usernames) == 0 && sizeof($groupnames) == 0)) { |
||
8248 | return -4; |
||
8249 | } |
||
8250 | |||
8251 | if ($mode != 'GRANT' && $mode != 'REVOKE') { |
||
8252 | return -5; |
||
8253 | } |
||
8254 | |||
8255 | $sql = $mode; |
||
8256 | |||
8257 | // Grant option |
||
8258 | if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) { |
||
8259 | $sql .= ' GRANT OPTION FOR'; |
||
8260 | } |
||
8261 | |||
8262 | if (in_array('ALL PRIVILEGES', $privileges)) { |
||
8263 | $sql .= ' ALL PRIVILEGES'; |
||
8264 | } else { |
||
8265 | if ($type == 'column') { |
||
8266 | $this->fieldClean($object); |
||
8267 | $sql .= ' ' . join(" (\"{$object}\"), ", $privileges); |
||
8268 | } else { |
||
8269 | $sql .= ' ' . join(', ', $privileges); |
||
8270 | } |
||
8271 | } |
||
8272 | |||
8273 | switch ($type) { |
||
8274 | case 'column': |
||
8275 | $sql .= " (\"{$object}\")"; |
||
8276 | $object = $table; |
||
8277 | case 'table': |
||
8278 | case 'view': |
||
8279 | case 'sequence': |
||
8280 | $this->fieldClean($object); |
||
8281 | $sql .= " ON \"{$f_schema}\".\"{$object}\""; |
||
8282 | break; |
||
8283 | case 'database': |
||
8284 | $this->fieldClean($object); |
||
8285 | $sql .= " ON DATABASE \"{$object}\""; |
||
8286 | break; |
||
8287 | case 'function': |
||
8288 | // Function comes in with $object as function OID |
||
8289 | $fn = $this->getFunction($object); |
||
8290 | $this->fieldClean($fn->fields['proname']); |
||
8291 | $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})"; |
||
8292 | break; |
||
8293 | case 'language': |
||
8294 | $this->fieldClean($object); |
||
8295 | $sql .= " ON LANGUAGE \"{$object}\""; |
||
8296 | break; |
||
8297 | case 'schema': |
||
8298 | $this->fieldClean($object); |
||
8299 | $sql .= " ON SCHEMA \"{$object}\""; |
||
8300 | break; |
||
8301 | case 'tablespace': |
||
8302 | $this->fieldClean($object); |
||
8303 | $sql .= " ON TABLESPACE \"{$object}\""; |
||
8304 | break; |
||
8305 | default: |
||
8306 | return -1; |
||
8307 | } |
||
8308 | |||
8309 | // Dump PUBLIC |
||
8310 | $first = true; |
||
8311 | $sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM '; |
||
8312 | if ($public) { |
||
8313 | $sql .= 'PUBLIC'; |
||
8314 | $first = false; |
||
8315 | } |
||
8316 | // Dump users |
||
8317 | foreach ($usernames as $v) { |
||
8318 | if ($first) { |
||
8319 | $sql .= "\"{$v}\""; |
||
8320 | $first = false; |
||
8321 | } else { |
||
8322 | $sql .= ", \"{$v}\""; |
||
8323 | } |
||
8324 | } |
||
8325 | // Dump groups |
||
8326 | foreach ($groupnames as $v) { |
||
8327 | if ($first) { |
||
8328 | $sql .= "GROUP \"{$v}\""; |
||
8329 | $first = false; |
||
8330 | } else { |
||
8331 | $sql .= ", GROUP \"{$v}\""; |
||
8332 | } |
||
8333 | } |
||
8334 | |||
8335 | // Grant option |
||
8336 | if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) { |
||
8337 | $sql .= ' WITH GRANT OPTION'; |
||
8338 | } |
||
8339 | |||
8340 | // Cascade revoke |
||
8341 | if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) { |
||
8342 | $sql .= ' CASCADE'; |
||
8343 | } |
||
8344 | |||
8345 | return $this->execute($sql); |
||
8346 | } |
||
8347 | |||
8348 | /** |
||
8349 | * Retrieves information for all tablespaces |
||
8350 | * |
||
8351 | * @param bool|\PHPPgAdmin\Database\Include $all Include all tablespaces (necessary when moving objects back to the default space) |
||
8352 | * @return \PHPPgAdmin\Database\A recordset |
||
8353 | */ |
||
8354 | public function getTablespaces($all = false) |
||
8355 | { |
||
8356 | $conf = $this->conf; |
||
8357 | |||
8358 | $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation, |
||
8359 | (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment |
||
8360 | FROM pg_catalog.pg_tablespace"; |
||
8361 | |||
8362 | if (!$conf['show_system'] && !$all) { |
||
8363 | $sql .= ' WHERE spcname NOT LIKE $$pg\_%$$'; |
||
8364 | } |
||
8365 | |||
8366 | $sql .= ' ORDER BY spcname'; |
||
8367 | |||
8368 | return $this->selectSet($sql); |
||
8369 | } |
||
8370 | |||
8371 | // Misc functions |
||
8372 | |||
8373 | /** |
||
8374 | * Retrieves a tablespace's information |
||
8375 | * |
||
8376 | * @param $spcname |
||
8377 | * @return \PHPPgAdmin\Database\A recordset |
||
8378 | */ |
||
8379 | public function getTablespace($spcname) |
||
8380 | { |
||
8381 | $this->clean($spcname); |
||
8382 | |||
8383 | $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation, |
||
8384 | (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment |
||
8385 | FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'"; |
||
8386 | |||
8387 | return $this->selectSet($sql); |
||
8388 | } |
||
8389 | |||
8390 | /** |
||
8391 | * Creates a tablespace |
||
8392 | * |
||
8393 | * @param $spcname The name of the tablespace to create |
||
8394 | * @param $spcowner The owner of the tablespace. '' for current |
||
8395 | * @param $spcloc The directory in which to create the tablespace |
||
8396 | * @param string $comment |
||
8397 | * @return int 0 success |
||
8398 | */ |
||
8399 | public function createTablespace($spcname, $spcowner, $spcloc, $comment = '') |
||
8400 | { |
||
8401 | $this->fieldClean($spcname); |
||
8402 | $this->clean($spcloc); |
||
8403 | |||
8404 | $sql = "CREATE TABLESPACE \"{$spcname}\""; |
||
8405 | |||
8406 | if ($spcowner != '') { |
||
8407 | $this->fieldClean($spcowner); |
||
8408 | $sql .= " OWNER \"{$spcowner}\""; |
||
8409 | } |
||
8410 | |||
8411 | $sql .= " LOCATION '{$spcloc}'"; |
||
8412 | |||
8413 | $status = $this->execute($sql); |
||
8414 | if ($status != 0) { |
||
8415 | return -1; |
||
8416 | } |
||
8417 | |||
8418 | View Code Duplication | if ($comment != '' && $this->hasSharedComments()) { |
|
8419 | $status = $this->setComment('TABLESPACE', $spcname, '', $comment); |
||
8420 | if ($status != 0) { |
||
8421 | return -2; |
||
8422 | } |
||
8423 | } |
||
8424 | |||
8425 | return 0; |
||
8426 | } |
||
8427 | |||
8428 | /** |
||
8429 | * Alters a tablespace |
||
8430 | * |
||
8431 | * @param $spcname The name of the tablespace |
||
8432 | * @param $name The new name for the tablespace |
||
8433 | * @param $owner The new owner for the tablespace |
||
8434 | * @param string $comment |
||
8435 | * @return bool|int 0 success |
||
8436 | */ |
||
8437 | public function alterTablespace($spcname, $name, $owner, $comment = '') |
||
8438 | { |
||
8439 | $this->fieldClean($spcname); |
||
8440 | $this->fieldClean($name); |
||
8441 | $this->fieldClean($owner); |
||
8442 | |||
8443 | // Begin transaction |
||
8444 | $status = $this->beginTransaction(); |
||
8445 | if ($status != 0) { |
||
8446 | return -1; |
||
8447 | } |
||
8448 | |||
8449 | // Owner |
||
8450 | $sql = "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\""; |
||
8451 | $status = $this->execute($sql); |
||
8452 | if ($status != 0) { |
||
8453 | $this->rollbackTransaction(); |
||
8454 | |||
8455 | return -2; |
||
8456 | } |
||
8457 | |||
8458 | // Rename (only if name has changed) |
||
8459 | if ($name != $spcname) { |
||
8460 | $sql = "ALTER TABLESPACE \"{$spcname}\" RENAME TO \"{$name}\""; |
||
8461 | $status = $this->execute($sql); |
||
8462 | if ($status != 0) { |
||
8463 | $this->rollbackTransaction(); |
||
8464 | |||
8465 | return -3; |
||
8466 | } |
||
8467 | |||
8468 | $spcname = $name; |
||
8469 | } |
||
8470 | |||
8471 | // Set comment if it has changed |
||
8472 | View Code Duplication | if (trim($comment) != '' && $this->hasSharedComments()) { |
|
8473 | $status = $this->setComment('TABLESPACE', $spcname, '', $comment); |
||
8474 | if ($status != 0) { |
||
8475 | return -4; |
||
8476 | } |
||
8477 | } |
||
8478 | |||
8479 | return $this->endTransaction(); |
||
8480 | } |
||
8481 | |||
8482 | /** |
||
8483 | * Drops a tablespace |
||
8484 | * |
||
8485 | * @param $spcname The name of the domain to drop |
||
8486 | * @return \PHPPgAdmin\Database\A 0 success |
||
8487 | */ |
||
8488 | public function dropTablespace($spcname) |
||
8489 | { |
||
8490 | $this->fieldClean($spcname); |
||
8491 | |||
8492 | $sql = "DROP TABLESPACE \"{$spcname}\""; |
||
8493 | |||
8494 | return $this->execute($sql); |
||
8495 | } |
||
8496 | |||
8497 | /** |
||
8498 | * Analyze a database |
||
8499 | * |
||
8500 | * @param $table (optional) The table to analyze |
||
8501 | * @return \PHPPgAdmin\Database\A |
||
8502 | */ |
||
8503 | View Code Duplication | public function analyzeDB($table = '') |
|
8504 | { |
||
8505 | if ($table != '') { |
||
8506 | $f_schema = $this->_schema; |
||
8507 | $this->fieldClean($f_schema); |
||
8508 | $this->fieldClean($table); |
||
8509 | |||
8510 | $sql = "ANALYZE \"{$f_schema}\".\"{$table}\""; |
||
8511 | } else { |
||
8512 | $sql = 'ANALYZE'; |
||
8513 | } |
||
8514 | |||
8515 | return $this->execute($sql); |
||
8516 | } |
||
8517 | |||
8518 | /** |
||
8519 | * Vacuums a database |
||
8520 | * |
||
8521 | * @param \PHPPgAdmin\Database\The|string $table The table to vacuum |
||
8522 | * @param bool|\PHPPgAdmin\Database\If $analyze If true, also does analyze |
||
8523 | * @param bool|\PHPPgAdmin\Database\If $full If true, selects "full" vacuum |
||
8524 | * @param bool|\PHPPgAdmin\Database\If $freeze If true, selects aggressive "freezing" of tuples |
||
8525 | * @return \PHPPgAdmin\Database\A |
||
8526 | */ |
||
8527 | public function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) |
||
8528 | { |
||
8529 | $sql = 'VACUUM'; |
||
8530 | if ($full) { |
||
8531 | $sql .= ' FULL'; |
||
8532 | } |
||
8533 | |||
8534 | if ($freeze) { |
||
8535 | $sql .= ' FREEZE'; |
||
8536 | } |
||
8537 | |||
8538 | if ($analyze) { |
||
8539 | $sql .= ' ANALYZE'; |
||
8540 | } |
||
8541 | |||
8542 | if ($table != '') { |
||
8543 | $f_schema = $this->_schema; |
||
8544 | $this->fieldClean($f_schema); |
||
8545 | $this->fieldClean($table); |
||
8546 | $sql .= " \"{$f_schema}\".\"{$table}\""; |
||
8547 | } |
||
8548 | |||
8549 | return $this->execute($sql); |
||
8550 | } |
||
8551 | |||
8552 | /** |
||
8553 | * Returns all autovacuum global configuration |
||
8554 | * |
||
8555 | * @return associative array array( param => value, ...) |
||
8556 | */ |
||
8557 | public function getAutovacuum() |
||
8558 | { |
||
8559 | $_defaults = $this->selectSet("SELECT name, setting |
||
8560 | FROM pg_catalog.pg_settings |
||
8561 | WHERE |
||
8562 | name = 'autovacuum' |
||
8563 | OR name = 'autovacuum_vacuum_threshold' |
||
8564 | OR name = 'autovacuum_vacuum_scale_factor' |
||
8565 | OR name = 'autovacuum_analyze_threshold' |
||
8566 | OR name = 'autovacuum_analyze_scale_factor' |
||
8567 | OR name = 'autovacuum_vacuum_cost_delay' |
||
8568 | OR name = 'autovacuum_vacuum_cost_limit' |
||
8569 | OR name = 'vacuum_freeze_min_age' |
||
8570 | OR name = 'autovacuum_freeze_max_age' |
||
8571 | " |
||
8572 | ); |
||
8573 | |||
8574 | $ret = []; |
||
8575 | while (!$_defaults->EOF) { |
||
8576 | $ret[$_defaults->fields['name']] = $_defaults->fields['setting']; |
||
8577 | $_defaults->moveNext(); |
||
8578 | } |
||
8579 | |||
8580 | return $ret; |
||
8581 | } |
||
8582 | |||
8583 | /** |
||
8584 | * Returns all available autovacuum per table information. |
||
8585 | * |
||
8586 | * @param $table |
||
8587 | * @param $vacenabled |
||
8588 | * @param $vacthreshold |
||
8589 | * @param $vacscalefactor |
||
8590 | * @param $anathresold |
||
8591 | * @param $anascalefactor |
||
8592 | * @param $vaccostdelay |
||
8593 | * @param $vaccostlimit |
||
8594 | * @return \PHPPgAdmin\Database\A recordset |
||
8595 | */ |
||
8596 | public function saveAutovacuum( |
||
8597 | $table, |
||
8598 | $vacenabled, |
||
8599 | $vacthreshold, |
||
8600 | $vacscalefactor, |
||
8601 | $anathresold, |
||
8602 | $anascalefactor, |
||
8603 | $vaccostdelay, |
||
8604 | $vaccostlimit |
||
8605 | ) { |
||
8606 | $f_schema = $this->_schema; |
||
8607 | $this->fieldClean($f_schema); |
||
8608 | $this->fieldClean($table); |
||
8609 | |||
8610 | $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET ("; |
||
8611 | |||
8612 | if (!empty($vacenabled)) { |
||
8613 | $this->clean($vacenabled); |
||
8614 | $params[] = "autovacuum_enabled='{$vacenabled}'"; |
||
8615 | } |
||
8616 | if (!empty($vacthreshold)) { |
||
8617 | $this->clean($vacthreshold); |
||
8618 | $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'"; |
||
8619 | } |
||
8620 | if (!empty($vacscalefactor)) { |
||
8621 | $this->clean($vacscalefactor); |
||
8622 | $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'"; |
||
8623 | } |
||
8624 | if (!empty($anathresold)) { |
||
8625 | $this->clean($anathresold); |
||
8626 | $params[] = "autovacuum_analyze_threshold='{$anathresold}'"; |
||
8627 | } |
||
8628 | if (!empty($anascalefactor)) { |
||
8629 | $this->clean($anascalefactor); |
||
8630 | $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'"; |
||
8631 | } |
||
8632 | if (!empty($vaccostdelay)) { |
||
8633 | $this->clean($vaccostdelay); |
||
8634 | $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'"; |
||
8635 | } |
||
8636 | if (!empty($vaccostlimit)) { |
||
8637 | $this->clean($vaccostlimit); |
||
8638 | $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'"; |
||
8639 | } |
||
8640 | |||
8641 | $sql = $sql . implode(',', $params) . ');'; |
||
8642 | |||
8643 | return $this->execute($sql); |
||
8644 | } |
||
8645 | |||
8646 | // Type conversion routines |
||
8647 | |||
8648 | public function dropAutovacuum($table) |
||
8649 | { |
||
8650 | $f_schema = $this->_schema; |
||
8651 | $this->fieldClean($f_schema); |
||
8652 | $this->fieldClean($table); |
||
8653 | |||
8654 | return $this->execute(" |
||
8655 | ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold, |
||
8656 | autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor, |
||
8657 | autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit |
||
8658 | );" |
||
8659 | ); |
||
8660 | } |
||
8661 | |||
8662 | /** |
||
8663 | * Returns all available process information. |
||
8664 | * |
||
8665 | * @param $database (optional) Find only connections to specified database |
||
8666 | * @return A recordset |
||
8667 | */ |
||
8668 | public function getProcesses($database = null) |
||
8669 | { |
||
8670 | if ($database === null) { |
||
8671 | $sql = "SELECT datname, usename, pid, waiting, state_change as query_start, |
||
8672 | case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query |
||
8673 | FROM pg_catalog.pg_stat_activity |
||
8674 | ORDER BY datname, usename, pid"; |
||
8675 | } else { |
||
8676 | $this->clean($database); |
||
8677 | $sql = "SELECT datname, usename, pid, waiting, state_change as query_start, |
||
8678 | case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query |
||
8679 | FROM pg_catalog.pg_stat_activity |
||
8680 | WHERE datname='{$database}' |
||
8681 | ORDER BY usename, pid"; |
||
8682 | } |
||
8683 | |||
8684 | return $this->selectSet($sql); |
||
8685 | } |
||
8686 | |||
8687 | // interfaces Statistics collector functions |
||
8688 | |||
8689 | /** |
||
8690 | * Returns table locks information in the current database |
||
8691 | * |
||
8692 | * @return A recordset |
||
8693 | */ |
||
8694 | |||
8695 | View Code Duplication | public function getLocks() |
|
8696 | { |
||
8697 | $conf = $this->conf; |
||
8698 | |||
8699 | if (!$conf['show_system']) { |
||
8700 | $where = 'AND pn.nspname NOT LIKE $$pg\_%$$'; |
||
8701 | } else { |
||
8702 | $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'"; |
||
8703 | } |
||
8704 | |||
8705 | $sql = " |
||
8706 | SELECT |
||
8707 | pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction, |
||
8708 | (select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid' |
||
8709 | and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction |
||
8710 | FROM |
||
8711 | pg_catalog.pg_locks pl, |
||
8712 | pg_catalog.pg_class pc, |
||
8713 | pg_catalog.pg_namespace pn |
||
8714 | WHERE |
||
8715 | pl.relation = pc.oid AND pc.relnamespace=pn.oid |
||
8716 | {$where} |
||
8717 | ORDER BY pid,nspname,tablename"; |
||
8718 | |||
8719 | return $this->selectSet($sql); |
||
8720 | } |
||
8721 | |||
8722 | /** |
||
8723 | * Sends a cancel or kill command to a process |
||
8724 | * |
||
8725 | * @param $pid The ID of the backend process |
||
8726 | * @param $signal 'CANCEL' |
||
8727 | * @return int 0 success |
||
8728 | */ |
||
8729 | public function sendSignal($pid, $signal) |
||
8730 | { |
||
8731 | // Clean |
||
8732 | $pid = (int) $pid; |
||
8733 | |||
8734 | if ($signal == 'CANCEL') { |
||
8735 | $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val"; |
||
8736 | } elseif ($signal == 'KILL') { |
||
8737 | $sql = "SELECT pg_catalog.pg_terminate_backend({$pid}) AS val"; |
||
8738 | } else { |
||
8739 | return -1; |
||
8740 | } |
||
8741 | |||
8742 | // Execute the query |
||
8743 | $val = $this->selectField($sql, 'val'); |
||
8744 | |||
8745 | if ($val === 'f') { |
||
8746 | return -1; |
||
8747 | } |
||
8748 | |||
8749 | if ($val === 't') { |
||
8750 | return 0; |
||
8751 | } else { |
||
8752 | return -1; |
||
8753 | } |
||
8754 | } |
||
8755 | |||
8756 | /** |
||
8757 | * Executes an SQL script as a series of SQL statements. Returns |
||
8758 | * the result of the final step. This is a very complicated lexer |
||
8759 | * based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in |
||
8760 | * the PostgreSQL source code. |
||
8761 | * XXX: It does not handle multibyte languages properly. |
||
8762 | * |
||
8763 | * @param $name Entry in $_FILES to use |
||
8764 | * @param $callback (optional) Callback function to call with each query, |
||
8765 | * its result and line number. |
||
8766 | * @return True for general success, false on any failure. |
||
8767 | */ |
||
8768 | public function executeScript($name, $callback = null) |
||
8769 | { |
||
8770 | |||
8771 | // This whole function isn't very encapsulated, but hey... |
||
8772 | $conn = $this->conn->_connectionID; |
||
8773 | if (!is_uploaded_file($_FILES[$name]['tmp_name'])) { |
||
8774 | return false; |
||
8775 | } |
||
8776 | |||
8777 | $fd = fopen($_FILES[$name]['tmp_name'], 'rb'); |
||
8778 | if (!$fd) { |
||
8779 | return false; |
||
8780 | } |
||
8781 | |||
8782 | // Build up each SQL statement, they can be multiline |
||
8783 | $query_buf = null; |
||
8784 | $query_start = 0; |
||
8785 | $in_quote = 0; |
||
8786 | $in_xcomment = 0; |
||
8787 | $bslash_count = 0; |
||
8788 | $dol_quote = null; |
||
8789 | $paren_level = 0; |
||
8790 | $len = 0; |
||
8791 | $i = 0; |
||
8792 | $prevlen = 0; |
||
8793 | $thislen = 0; |
||
8794 | $lineno = 0; |
||
8795 | |||
8796 | // Loop over each line in the file |
||
8797 | while (!feof($fd)) { |
||
8798 | $line = fgets($fd); |
||
8799 | $lineno++; |
||
8800 | |||
8801 | // Nothing left on line? Then ignore... |
||
8802 | if (trim($line) == '') { |
||
8803 | continue; |
||
8804 | } |
||
8805 | |||
8806 | $len = strlen($line); |
||
8807 | $query_start = 0; |
||
8808 | |||
8809 | /* |
||
8810 | * Parse line, looking for command separators. |
||
8811 | * |
||
8812 | * The current character is at line[i], the prior character at line[i |
||
8813 | * - prevlen], the next character at line[i + thislen]. |
||
8814 | */ |
||
8815 | $prevlen = 0; |
||
8816 | $thislen = ($len > 0) ? 1 : 0; |
||
8817 | |||
8818 | for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) { |
||
8819 | |||
8820 | /* was the previous character a backslash? */ |
||
8821 | if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\') { |
||
8822 | $bslash_count++; |
||
8823 | } else { |
||
8824 | $bslash_count = 0; |
||
8825 | } |
||
8826 | |||
8827 | /* |
||
8828 | * It is important to place the in_* test routines before the |
||
8829 | * in_* detection routines. i.e. we have to test if we are in |
||
8830 | * a quote before testing for comments. |
||
8831 | */ |
||
8832 | |||
8833 | /* in quote? */ |
||
8834 | if ($in_quote !== 0) { |
||
8835 | /* |
||
8836 | * end of quote if matching non-backslashed character. |
||
8837 | * backslashes don't count for double quotes, though. |
||
8838 | */ |
||
8839 | if (substr($line, $i, 1) == $in_quote && |
||
8840 | ($bslash_count % 2 == 0 || $in_quote == '"')) { |
||
8841 | $in_quote = 0; |
||
8842 | } |
||
8843 | } /* in or end of $foo$ type quote? */ |
||
8844 | else { |
||
8845 | if ($dol_quote) { |
||
8846 | if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) { |
||
8847 | $this->advance_1($i, $prevlen, $thislen); |
||
8848 | while (substr($line, $i, 1) != '$') { |
||
8849 | $this->advance_1($i, $prevlen, $thislen); |
||
8850 | } |
||
8851 | |||
8852 | $dol_quote = null; |
||
8853 | } |
||
8854 | } /* start of extended comment? */ |
||
8855 | else { |
||
8856 | if (substr($line, $i, 2) == '/*') { |
||
8857 | $in_xcomment++; |
||
8858 | if ($in_xcomment == 1) { |
||
8859 | $this->advance_1($i, $prevlen, $thislen); |
||
8860 | } |
||
8861 | } /* in or end of extended comment? */ |
||
8862 | else { |
||
8863 | if ($in_xcomment) { |
||
8864 | if (substr($line, $i, 2) == '*/' && !--$in_xcomment) { |
||
8865 | $this->advance_1($i, $prevlen, $thislen); |
||
8866 | } |
||
8867 | } /* start of quote? */ |
||
8868 | else { |
||
8869 | if (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') { |
||
8870 | $in_quote = substr($line, $i, 1); |
||
8871 | } /* |
||
8872 | * start of $foo$ type quote? |
||
8873 | */ |
||
8874 | else { |
||
8875 | if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) { |
||
8876 | $dol_end = strpos(substr($line, $i + 1), '$'); |
||
8877 | $dol_quote = substr($line, $i, $dol_end + 1); |
||
8878 | $this->advance_1($i, $prevlen, $thislen); |
||
8879 | while (substr($line, $i, 1) != '$') { |
||
8880 | $this->advance_1($i, $prevlen, $thislen); |
||
8881 | } |
||
8882 | } /* single-line comment? truncate line */ |
||
8883 | else { |
||
8884 | if (substr($line, $i, 2) == '--') { |
||
8885 | $line = substr($line, 0, $i); /* remove comment */ |
||
8886 | break; |
||
8887 | } /* count nested parentheses */ |
||
8888 | |||
8889 | if (substr($line, $i, 1) == '(') { |
||
8890 | $paren_level++; |
||
8891 | } else { |
||
8892 | if (substr($line, $i, 1) == ')' && $paren_level > 0) { |
||
8893 | $paren_level--; |
||
8894 | } /* semicolon? then send query */ |
||
8895 | else { |
||
8896 | if (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level) { |
||
8897 | $subline = substr(substr($line, 0, $i), $query_start); |
||
8898 | /* |
||
8899 | * insert a cosmetic newline, if this is not the first |
||
8900 | * line in the buffer |
||
8901 | */ |
||
8902 | if (strlen($query_buf) > 0) { |
||
8903 | $query_buf .= "\n"; |
||
8904 | } |
||
8905 | |||
8906 | /* append the line to the query buffer */ |
||
8907 | $query_buf .= $subline; |
||
8908 | /* is there anything in the query_buf? */ |
||
8909 | if (trim($query_buf)) { |
||
8910 | $query_buf .= ';'; |
||
8911 | |||
8912 | // Execute the query. PHP cannot execute |
||
8913 | // empty queries, unlike libpq |
||
8914 | $res = @pg_query($conn, $query_buf); |
||
8915 | |||
8916 | // Call the callback function for display |
||
8917 | if ($callback !== null) { |
||
8918 | $callback($query_buf, $res, $lineno); |
||
8919 | } |
||
8920 | |||
8921 | // Check for COPY request |
||
8922 | View Code Duplication | if (pg_result_status($res) == 4) { |
|
8923 | // 4 == PGSQL_COPY_FROM |
||
8924 | while (!feof($fd)) { |
||
8925 | $copy = fgets($fd, 32768); |
||
8926 | $lineno++; |
||
8927 | pg_put_line($conn, $copy); |
||
8928 | if ($copy == "\\.\n" || $copy == "\\.\r\n") { |
||
8929 | pg_end_copy($conn); |
||
8930 | break; |
||
8931 | } |
||
8932 | } |
||
8933 | } |
||
8934 | } |
||
8935 | $query_buf = null; |
||
8936 | $query_start = $i + $thislen; |
||
8937 | } |
||
8938 | |||
8939 | /* |
||
8940 | * keyword or identifier? |
||
8941 | * We grab the whole string so that we don't |
||
8942 | * mistakenly see $foo$ inside an identifier as the start |
||
8943 | * of a dollar quote. |
||
8944 | */ |
||
8945 | // XXX: multibyte here |
||
8946 | else { |
||
8947 | if (preg_match('/^[_[:alpha:]]$/', substr($line, $i, 1))) { |
||
8948 | $sub = substr($line, $i, $thislen); |
||
8949 | while (preg_match('/^[\$_A-Za-z0-9]$/', $sub)) { |
||
8950 | /* keep going while we still have identifier chars */ |
||
8951 | $this->advance_1($i, $prevlen, $thislen); |
||
8952 | $sub = substr($line, $i, $thislen); |
||
8953 | } |
||
8954 | // Since we're now over the next character to be examined, it is necessary |
||
8955 | // to move back one space. |
||
8956 | $i -= $prevlen; |
||
8957 | } |
||
8958 | } |
||
8959 | } |
||
8960 | } |
||
8961 | } |
||
8962 | } |
||
8963 | } |
||
8964 | } |
||
8965 | } |
||
8966 | } |
||
8967 | } // end for |
||
8968 | |||
8969 | /* Put the rest of the line in the query buffer. */ |
||
8970 | $subline = substr($line, $query_start); |
||
8971 | if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline)) { |
||
8972 | if (strlen($query_buf) > 0) { |
||
8973 | $query_buf .= "\n"; |
||
8974 | } |
||
8975 | |||
8976 | $query_buf .= $subline; |
||
8977 | } |
||
8978 | |||
8979 | $line = null; |
||
8980 | } // end while |
||
8981 | |||
8982 | /* |
||
8983 | * Process query at the end of file without a semicolon, so long as |
||
8984 | * it's non-empty. |
||
8985 | */ |
||
8986 | if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf)) { |
||
8987 | // Execute the query |
||
8988 | $res = @pg_query($conn, $query_buf); |
||
8989 | |||
8990 | // Call the callback function for display |
||
8991 | if ($callback !== null) { |
||
8992 | $callback($query_buf, $res, $lineno); |
||
8993 | } |
||
8994 | |||
8995 | // Check for COPY request |
||
8996 | View Code Duplication | if (pg_result_status($res) == 4) { |
|
8997 | // 4 == PGSQL_COPY_FROM |
||
8998 | while (!feof($fd)) { |
||
8999 | $copy = fgets($fd, 32768); |
||
9000 | $lineno++; |
||
9001 | pg_put_line($conn, $copy); |
||
9002 | if ($copy == "\\.\n" || $copy == "\\.\r\n") { |
||
9003 | pg_end_copy($conn); |
||
9004 | break; |
||
9005 | } |
||
9006 | } |
||
9007 | } |
||
9008 | } |
||
9009 | |||
9010 | fclose($fd); |
||
9011 | |||
9012 | return true; |
||
9013 | } |
||
9014 | |||
9015 | /** |
||
9016 | * A private helper method for executeScript that advances the |
||
9017 | * character by 1. In psql this is careful to take into account |
||
9018 | * multibyte languages, but we don't at the moment, so this function |
||
9019 | * is someone redundant, since it will always advance by 1 |
||
9020 | * |
||
9021 | * @param &$i The current character position in the line |
||
9022 | * @param &$prevlen Length of previous character (ie. 1) |
||
9023 | * @param &$thislen Length of current character (ie. 1) |
||
9024 | */ |
||
9025 | private function advance_1(&$i, &$prevlen, &$thislen) |
||
9026 | { |
||
9027 | $prevlen = $thislen; |
||
9028 | $i += $thislen; |
||
9029 | $thislen = 1; |
||
9030 | } |
||
9031 | |||
9032 | /** |
||
9033 | * Private helper method to detect a valid $foo$ quote delimiter at |
||
9034 | * the start of the parameter dquote |
||
9035 | * |
||
9036 | * @param $dquote |
||
9037 | * @return True if valid, false otherwise |
||
9038 | */ |
||
9039 | private function valid_dolquote($dquote) |
||
9040 | { |
||
9041 | // XXX: support multibyte |
||
9042 | return (preg_match('/^[$][$]/', $dquote) || preg_match('/^[$][_[:alpha:]][_[:alnum:]]*[$]/', $dquote)); |
||
9043 | } |
||
9044 | |||
9045 | // Capabilities |
||
9046 | |||
9047 | /** |
||
9048 | * Returns a recordset of all columns in a query. Supports paging. |
||
9049 | * |
||
9050 | * @param $type Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier, |
||
9051 | * or 'SELECT" if it's a select query |
||
9052 | * @param $table The base table of the query. NULL for no table. |
||
9053 | * @param $query The query that is being executed. NULL for no query. |
||
9054 | * @param $sortkey The column number to sort by, or '' or null for no sorting |
||
9055 | * @param $sortdir The direction in which to sort the specified column ('asc' or 'desc') |
||
9056 | * @param $page The page of the relation to retrieve |
||
9057 | * @param $page_size The number of rows per page |
||
9058 | * @param &$max_pages (return-by-ref) The max number of pages in the relation |
||
9059 | * @return A recordset on success |
||
9060 | * @return -1 transaction error |
||
9061 | * @return -2 counting error |
||
9062 | * @return -3 page or page_size invalid |
||
9063 | * @return -4 unknown type |
||
9064 | * @return -5 failed setting transaction read only |
||
9065 | */ |
||
9066 | public function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages) |
||
9067 | { |
||
9068 | // Check that we're not going to divide by zero |
||
9069 | if (!is_numeric($page_size) || $page_size != (int) $page_size || $page_size <= 0) { |
||
9070 | return -3; |
||
9071 | } |
||
9072 | |||
9073 | // If $type is TABLE, then generate the query |
||
9074 | switch ($type) { |
||
9075 | case 'TABLE': |
||
9076 | if (preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) { |
||
9077 | $orderby = [$sortkey => $sortdir]; |
||
9078 | } else { |
||
9079 | $orderby = []; |
||
9080 | } |
||
9081 | |||
9082 | $query = $this->getSelectSQL($table, [], [], [], $orderby); |
||
9083 | break; |
||
9084 | case 'QUERY': |
||
9085 | case 'SELECT': |
||
9086 | // Trim query |
||
9087 | $query = trim($query); |
||
9088 | // Trim off trailing semi-colon if there is one |
||
9089 | if (substr($query, strlen($query) - 1, 1) == ';') { |
||
9090 | $query = substr($query, 0, strlen($query) - 1); |
||
9091 | } |
||
9092 | |||
9093 | break; |
||
9094 | default: |
||
9095 | return -4; |
||
9096 | } |
||
9097 | |||
9098 | // Generate count query |
||
9099 | $count = "SELECT COUNT(*) AS total FROM ($query) AS sub"; |
||
9100 | |||
9101 | // Open a transaction |
||
9102 | $status = $this->beginTransaction(); |
||
9103 | if ($status != 0) { |
||
9104 | return -1; |
||
9105 | } |
||
9106 | |||
9107 | // If backend supports read only queries, then specify read only mode |
||
9108 | // to avoid side effects from repeating queries that do writes. |
||
9109 | if ($this->hasReadOnlyQueries()) { |
||
9110 | $status = $this->execute('SET TRANSACTION READ ONLY'); |
||
9111 | if ($status != 0) { |
||
9112 | $this->rollbackTransaction(); |
||
9113 | |||
9114 | return -5; |
||
9115 | } |
||
9116 | } |
||
9117 | |||
9118 | // Count the number of rows |
||
9119 | $total = $this->browseQueryCount($query, $count); |
||
9120 | if ($total < 0) { |
||
9121 | $this->rollbackTransaction(); |
||
9122 | |||
9123 | return -2; |
||
9124 | } |
||
9125 | |||
9126 | // Calculate max pages |
||
9127 | $max_pages = ceil($total / $page_size); |
||
9128 | |||
9129 | // Check that page is less than or equal to max pages |
||
9130 | if (!is_numeric($page) || $page != (int) $page || $page > $max_pages || $page < 1) { |
||
9131 | $this->rollbackTransaction(); |
||
9132 | |||
9133 | return -3; |
||
9134 | } |
||
9135 | |||
9136 | // Set fetch mode to NUM so that duplicate field names are properly returned |
||
9137 | // for non-table queries. Since the SELECT feature only allows selecting one |
||
9138 | // table, duplicate fields shouldn't appear. |
||
9139 | if ($type == 'QUERY') { |
||
9140 | $this->conn->setFetchMode(ADODB_FETCH_NUM); |
||
9141 | } |
||
9142 | |||
9143 | // Figure out ORDER BY. Sort key is always the column number (based from one) |
||
9144 | // of the column to order by. Only need to do this for non-TABLE queries |
||
9145 | if ($type != 'TABLE' && preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) { |
||
9146 | $orderby = " ORDER BY {$sortkey}"; |
||
9147 | // Add sort order |
||
9148 | if ($sortdir == 'desc') { |
||
9149 | $orderby .= ' DESC'; |
||
9150 | } else { |
||
9151 | $orderby .= ' ASC'; |
||
9152 | } |
||
9153 | } else { |
||
9154 | $orderby = ''; |
||
9155 | } |
||
9156 | |||
9157 | // Actually retrieve the rows, with offset and limit |
||
9158 | $rs = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size); |
||
9159 | $status = $this->endTransaction(); |
||
9160 | if ($status != 0) { |
||
9161 | $this->rollbackTransaction(); |
||
9162 | |||
9163 | return -1; |
||
9164 | } |
||
9165 | |||
9166 | return $rs; |
||
9167 | } |
||
9168 | |||
9169 | /** |
||
9170 | * Generates the SQL for the 'select' function |
||
9171 | * |
||
9172 | * @param $table The table from which to select |
||
9173 | * @param $show An array of columns to show. Empty array means all columns. |
||
9174 | * @param $values An array mapping columns to values |
||
9175 | * @param $ops An array of the operators to use |
||
9176 | * @param $orderby (optional) An array of column numbers or names (one based) |
||
9177 | * mapped to sort direction (asc or desc or '' or null) to order by |
||
9178 | * @return The SQL query |
||
9179 | */ |
||
9180 | public function getSelectSQL($table, $show, $values, $ops, $orderby = []) |
||
9181 | { |
||
9182 | $this->fieldArrayClean($show); |
||
9183 | |||
9184 | // If an empty array is passed in, then show all columns |
||
9185 | if (sizeof($show) == 0) { |
||
9186 | if ($this->hasObjectID($table)) { |
||
9187 | $sql = "SELECT \"{$this->id}\", * FROM "; |
||
9188 | } else { |
||
9189 | $sql = 'SELECT * FROM '; |
||
9190 | } |
||
9191 | } else { |
||
9192 | // Add oid column automatically to results for editing purposes |
||
9193 | if (!in_array($this->id, $show) && $this->hasObjectID($table)) { |
||
9194 | $sql = "SELECT \"{$this->id}\", \""; |
||
9195 | } else { |
||
9196 | $sql = 'SELECT "'; |
||
9197 | } |
||
9198 | |||
9199 | $sql .= join('","', $show) . '" FROM '; |
||
9200 | } |
||
9201 | |||
9202 | $this->fieldClean($table); |
||
9203 | |||
9204 | if (isset($_REQUEST['schema'])) { |
||
9205 | $f_schema = $_REQUEST['schema']; |
||
9206 | $this->fieldClean($f_schema); |
||
9207 | $sql .= "\"{$f_schema}\"."; |
||
9208 | } |
||
9209 | $sql .= "\"{$table}\""; |
||
9210 | |||
9211 | // If we have values specified, add them to the WHERE clause |
||
9212 | $first = true; |
||
9213 | if (is_array($values) && sizeof($values) > 0) { |
||
9214 | foreach ($values as $k => $v) { |
||
9215 | if ($v != '' || $this->selectOps[$ops[$k]] == 'p') { |
||
9216 | $this->fieldClean($k); |
||
9217 | if ($first) { |
||
9218 | $sql .= ' WHERE '; |
||
9219 | $first = false; |
||
9220 | } else { |
||
9221 | $sql .= ' AND '; |
||
9222 | } |
||
9223 | // Different query format depending on operator type |
||
9224 | switch ($this->selectOps[$ops[$k]]) { |
||
9225 | case 'i': |
||
9226 | // Only clean the field for the inline case |
||
9227 | // this is because (x), subqueries need to |
||
9228 | // to allow 'a','b' as input. |
||
9229 | $this->clean($v); |
||
9230 | $sql .= "\"{$k}\" {$ops[$k]} '{$v}'"; |
||
9231 | break; |
||
9232 | case 'p': |
||
9233 | $sql .= "\"{$k}\" {$ops[$k]}"; |
||
9234 | break; |
||
9235 | case 'x': |
||
9236 | $sql .= "\"{$k}\" {$ops[$k]} ({$v})"; |
||
9237 | break; |
||
9238 | case 't': |
||
9239 | $sql .= "\"{$k}\" {$ops[$k]}('{$v}')"; |
||
9240 | break; |
||
9241 | default: |
||
9242 | // Shouldn't happen |
||
9243 | } |
||
9244 | } |
||
9245 | } |
||
9246 | } |
||
9247 | |||
9248 | // ORDER BY |
||
9249 | if (is_array($orderby) && sizeof($orderby) > 0) { |
||
9250 | $sql .= ' ORDER BY '; |
||
9251 | $first = true; |
||
9252 | foreach ($orderby as $k => $v) { |
||
9253 | if ($first) { |
||
9254 | $first = false; |
||
9255 | } else { |
||
9256 | $sql .= ', '; |
||
9257 | } |
||
9258 | |||
9259 | if (preg_match('/^[0-9]+$/', $k)) { |
||
9260 | $sql .= $k; |
||
9261 | } else { |
||
9262 | $this->fieldClean($k); |
||
9263 | $sql .= '"' . $k . '"'; |
||
9264 | } |
||
9265 | if (strtoupper($v) == 'DESC') { |
||
9266 | $sql .= ' DESC'; |
||
9267 | } |
||
9268 | } |
||
9269 | } |
||
9270 | |||
9271 | return $sql; |
||
9272 | } |
||
9273 | |||
9274 | public function hasReadOnlyQueries() |
||
9275 | { |
||
9276 | return true; |
||
9277 | } |
||
9278 | |||
9279 | /** |
||
9280 | * Finds the number of rows that would be returned by a |
||
9281 | * query. |
||
9282 | * |
||
9283 | * @param $query The SQL query |
||
9284 | * @param $count The count query |
||
9285 | * @return The count of rows |
||
9286 | * @return -1 error |
||
9287 | */ |
||
9288 | public function browseQueryCount($query, $count) |
||
9289 | { |
||
9290 | return $this->selectField($count, 'total'); |
||
9291 | } |
||
9292 | |||
9293 | /** |
||
9294 | * Returns a recordset of all columns in a table |
||
9295 | * |
||
9296 | * @param $table The name of a table |
||
9297 | * @param $key The associative array holding the key to retrieve |
||
9298 | * @return A recordset |
||
9299 | */ |
||
9300 | public function browseRow($table, $key) |
||
9301 | { |
||
9302 | $f_schema = $this->_schema; |
||
9303 | $this->fieldClean($f_schema); |
||
9304 | $this->fieldClean($table); |
||
9305 | |||
9306 | $sql = "SELECT * FROM \"{$f_schema}\".\"{$table}\""; |
||
9307 | if (is_array($key) && sizeof($key) > 0) { |
||
9308 | $sql .= ' WHERE true'; |
||
9309 | foreach ($key as $k => $v) { |
||
9310 | $this->fieldClean($k); |
||
9311 | $this->clean($v); |
||
9312 | $sql .= " AND \"{$k}\"='{$v}'"; |
||
9313 | } |
||
9314 | } |
||
9315 | |||
9316 | return $this->selectSet($sql); |
||
9317 | } |
||
9318 | |||
9319 | /** |
||
9320 | * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false |
||
9321 | * |
||
9322 | * @param $parameter the parameter |
||
9323 | * @return \PHPPgAdmin\Database\the|string |
||
9324 | */ |
||
9325 | public function dbBool(&$parameter) |
||
9326 | { |
||
9327 | if ($parameter) { |
||
9328 | $parameter = 't'; |
||
9329 | } else { |
||
9330 | $parameter = 'f'; |
||
9331 | } |
||
9332 | |||
9333 | return $parameter; |
||
9334 | } |
||
9335 | |||
9336 | /** |
||
9337 | * Fetches statistics for a database |
||
9338 | * |
||
9339 | * @param $database The database to fetch stats for |
||
9340 | * @return A recordset |
||
9341 | */ |
||
9342 | public function getStatsDatabase($database) |
||
9343 | { |
||
9344 | $this->clean($database); |
||
9345 | |||
9346 | $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'"; |
||
9347 | |||
9348 | return $this->selectSet($sql); |
||
9349 | } |
||
9350 | |||
9351 | /** |
||
9352 | * Fetches tuple statistics for a table |
||
9353 | * |
||
9354 | * @param $table The table to fetch stats for |
||
9355 | * @return A recordset |
||
9356 | */ |
||
9357 | View Code Duplication | public function getStatsTableTuples($table) |
|
9358 | { |
||
9359 | $c_schema = $this->_schema; |
||
9360 | $this->clean($c_schema); |
||
9361 | $this->clean($table); |
||
9362 | |||
9363 | $sql = "SELECT * FROM pg_stat_all_tables |
||
9364 | WHERE schemaname='{$c_schema}' AND relname='{$table}'"; |
||
9365 | |||
9366 | return $this->selectSet($sql); |
||
9367 | } |
||
9368 | |||
9369 | /** |
||
9370 | * Fetches I/0 statistics for a table |
||
9371 | * |
||
9372 | * @param $table The table to fetch stats for |
||
9373 | * @return A recordset |
||
9374 | */ |
||
9375 | View Code Duplication | public function getStatsTableIO($table) |
|
9376 | { |
||
9377 | $c_schema = $this->_schema; |
||
9378 | $this->clean($c_schema); |
||
9379 | $this->clean($table); |
||
9380 | |||
9381 | $sql = "SELECT * FROM pg_statio_all_tables |
||
9382 | WHERE schemaname='{$c_schema}' AND relname='{$table}'"; |
||
9383 | |||
9384 | return $this->selectSet($sql); |
||
9385 | } |
||
9386 | |||
9387 | /** |
||
9388 | * Fetches tuple statistics for all indexes on a table |
||
9389 | * |
||
9390 | * @param $table The table to fetch index stats for |
||
9391 | * @return A recordset |
||
9392 | */ |
||
9393 | View Code Duplication | public function getStatsIndexTuples($table) |
|
9394 | { |
||
9395 | $c_schema = $this->_schema; |
||
9396 | $this->clean($c_schema); |
||
9397 | $this->clean($table); |
||
9398 | |||
9399 | $sql = "SELECT * FROM pg_stat_all_indexes |
||
9400 | WHERE schemaname='{$c_schema}' AND relname='{$table}' ORDER BY indexrelname"; |
||
9401 | |||
9402 | return $this->selectSet($sql); |
||
9403 | } |
||
9404 | |||
9405 | /** |
||
9406 | * Fetches I/0 statistics for all indexes on a table |
||
9407 | * |
||
9408 | * @param $table The table to fetch index stats for |
||
9409 | * @return A recordset |
||
9410 | */ |
||
9411 | View Code Duplication | public function getStatsIndexIO($table) |
|
9412 | { |
||
9413 | $c_schema = $this->_schema; |
||
9414 | $this->clean($c_schema); |
||
9415 | $this->clean($table); |
||
9416 | |||
9417 | $sql = "SELECT * FROM pg_statio_all_indexes |
||
9418 | WHERE schemaname='{$c_schema}' AND relname='{$table}' |
||
9419 | ORDER BY indexrelname"; |
||
9420 | |||
9421 | return $this->selectSet($sql); |
||
9422 | } |
||
9423 | |||
9424 | public function hasAggregateSortOp() |
||
9425 | { |
||
9426 | return true; |
||
9427 | } |
||
9428 | |||
9429 | public function hasAlterAggregate() |
||
9430 | { |
||
9431 | return true; |
||
9432 | } |
||
9433 | |||
9434 | public function hasAlterColumnType() |
||
9435 | { |
||
9436 | return true; |
||
9437 | } |
||
9438 | |||
9439 | public function hasAlterDatabaseOwner() |
||
9440 | { |
||
9441 | return true; |
||
9442 | } |
||
9443 | |||
9444 | public function hasAlterSchema() |
||
9445 | { |
||
9446 | return true; |
||
9447 | } |
||
9448 | |||
9449 | public function hasAlterSchemaOwner() |
||
9450 | { |
||
9451 | return true; |
||
9452 | } |
||
9453 | |||
9454 | public function hasAlterSequenceSchema() |
||
9455 | { |
||
9456 | return true; |
||
9457 | } |
||
9458 | |||
9459 | public function hasAlterSequenceStart() |
||
9460 | { |
||
9461 | return true; |
||
9462 | } |
||
9463 | |||
9464 | public function hasAlterTableSchema() |
||
9465 | { |
||
9466 | return true; |
||
9467 | } |
||
9468 | |||
9469 | public function hasAutovacuum() |
||
9470 | { |
||
9471 | return true; |
||
9472 | } |
||
9473 | |||
9474 | public function hasCreateTableLike() |
||
9475 | { |
||
9476 | return true; |
||
9477 | } |
||
9478 | |||
9479 | public function hasDisableTriggers() |
||
9480 | { |
||
9481 | return true; |
||
9482 | } |
||
9483 | |||
9484 | public function hasAlterDomains() |
||
9485 | { |
||
9486 | return true; |
||
9487 | } |
||
9488 | |||
9489 | public function hasEnumTypes() |
||
9490 | { |
||
9491 | return true; |
||
9492 | } |
||
9493 | |||
9494 | public function hasFTS() |
||
9495 | { |
||
9496 | return true; |
||
9497 | } |
||
9498 | |||
9499 | public function hasFunctionCosting() |
||
9500 | { |
||
9501 | return true; |
||
9502 | } |
||
9503 | |||
9504 | public function hasFunctionGUC() |
||
9505 | { |
||
9506 | return true; |
||
9507 | } |
||
9508 | |||
9509 | public function hasNamedParams() |
||
9510 | { |
||
9511 | return true; |
||
9512 | } |
||
9513 | |||
9514 | public function hasPrepare() |
||
9515 | { |
||
9516 | return true; |
||
9517 | } |
||
9518 | |||
9519 | public function hasPreparedXacts() |
||
9520 | { |
||
9521 | return true; |
||
9522 | } |
||
9523 | |||
9524 | public function hasRecluster() |
||
9525 | { |
||
9526 | return true; |
||
9527 | } |
||
9528 | |||
9529 | public function hasServerAdminFuncs() |
||
9530 | { |
||
9531 | return true; |
||
9532 | } |
||
9533 | |||
9534 | public function hasQueryCancel() |
||
9535 | { |
||
9536 | return true; |
||
9537 | } |
||
9538 | |||
9539 | public function hasUserRename() |
||
9540 | { |
||
9541 | return true; |
||
9542 | } |
||
9543 | |||
9544 | public function hasUserSignals() |
||
9545 | { |
||
9546 | return true; |
||
9547 | } |
||
9548 | |||
9549 | public function hasVirtualTransactionId() |
||
9550 | { |
||
9551 | return true; |
||
9552 | } |
||
9553 | |||
9554 | public function hasAlterDatabase() |
||
9555 | { |
||
9556 | return $this->hasAlterDatabaseRename(); |
||
9557 | } |
||
9558 | |||
9559 | public function hasAlterDatabaseRename() |
||
9560 | { |
||
9561 | return true; |
||
9562 | } |
||
9563 | |||
9564 | public function hasDatabaseCollation() |
||
9567 | } |
||
9568 | |||
9569 | public function hasMagicTypes() |
||
9570 | { |
||
9571 | return true; |
||
9572 | } |
||
9573 | |||
9574 | public function hasQueryKill() |
||
9575 | { |
||
9576 | return true; |
||
9577 | } |
||
9578 | |||
9579 | public function hasConcurrentIndexBuild() |
||
9580 | { |
||
9581 | return true; |
||
9582 | } |
||
9583 | |||
9584 | public function hasForceReindex() |
||
9585 | { |
||
9586 | return false; |
||
9587 | } |
||
9588 | |||
9589 | public function hasByteaHexDefault() |
||
9590 | { |
||
9591 | return true; |
||
9592 | } |
||
9593 | } |
||
9594 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.