1 | <?php |
||
2 | |||
3 | /** |
||
4 | * PHPPgAdmin 6.1.3 |
||
5 | */ |
||
6 | |||
7 | namespace PHPPgAdmin\Database\Traits; |
||
8 | |||
9 | /** |
||
10 | * Common trait for full text search manipulation. |
||
11 | */ |
||
12 | trait FunctionTrait |
||
13 | { |
||
14 | /** |
||
15 | * Returns a list of all functions in the database. |
||
16 | * |
||
17 | * @param bool $all If true, will find all available functions, if false just those in search path |
||
18 | * @param mixed $type If truthy, will return functions of type trigger |
||
19 | * |
||
20 | * @return int|\PHPPgAdmin\ADORecordSet |
||
21 | */ |
||
22 | public function getFunctions($all = false, $type = null) |
||
23 | { |
||
24 | if ($all) { |
||
25 | $where = 'pg_catalog.pg_function_is_visible(p.oid)'; |
||
26 | $distinct = 'DISTINCT ON (p.proname)'; |
||
27 | |||
28 | if ($type) { |
||
29 | $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') "; |
||
30 | } |
||
31 | } else { |
||
32 | $c_schema = $this->_schema; |
||
33 | $this->clean($c_schema); |
||
34 | $where = "n.nspname = '{$c_schema}'"; |
||
35 | $distinct = ''; |
||
36 | } |
||
37 | |||
38 | $sql = " |
||
39 | SELECT |
||
40 | {$distinct} |
||
41 | p.oid AS prooid, |
||
42 | p.proname, |
||
43 | p.proretset, |
||
44 | pg_catalog.format_type(p.prorettype, NULL) AS proresult, |
||
45 | pg_catalog.oidvectortypes(p.proargtypes) AS proarguments, |
||
46 | pl.lanname AS prolanguage, |
||
47 | pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment, |
||
48 | p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto, |
||
49 | CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns, |
||
50 | coalesce(u.usename::text,p.proowner::text) AS proowner |
||
51 | |||
52 | FROM pg_catalog.pg_proc p |
||
53 | INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace |
||
54 | INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang |
||
55 | LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner |
||
56 | WHERE NOT p.proisagg |
||
57 | AND {$where} |
||
58 | ORDER BY p.proname, proresult |
||
59 | "; |
||
60 | |||
61 | return $this->selectSet($sql); |
||
62 | } |
||
63 | |||
64 | /** |
||
65 | * Returns a list of all functions that can be used in triggers. |
||
66 | * |
||
67 | * @return \PHPPgAdmin\ADORecordSet Functions that can be used in a trigger |
||
68 | */ |
||
69 | public function getTriggerFunctions() |
||
70 | { |
||
71 | return $this->getFunctions(true, 'trigger'); |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
![]() |
|||
72 | } |
||
73 | |||
74 | /** |
||
75 | * Returns an array containing a function's properties. |
||
76 | * |
||
77 | * @param array $f The array of data for the function |
||
78 | * |
||
79 | * @return array|int An array containing the properties, or -1 in case of error |
||
80 | */ |
||
81 | public function getFunctionProperties($f) |
||
82 | { |
||
83 | $temp = []; |
||
84 | |||
85 | // Volatility |
||
86 | if ('v' === $f['provolatile']) { |
||
87 | $temp[] = 'VOLATILE'; |
||
88 | } elseif ('i' === $f['provolatile']) { |
||
89 | $temp[] = 'IMMUTABLE'; |
||
90 | } elseif ('s' === $f['provolatile']) { |
||
91 | $temp[] = 'STABLE'; |
||
92 | } else { |
||
93 | return -1; |
||
94 | } |
||
95 | |||
96 | // Null handling |
||
97 | $f['proisstrict'] = $this->phpBool($f['proisstrict']); |
||
98 | |||
99 | if ($f['proisstrict']) { |
||
100 | $temp[] = 'RETURNS NULL ON NULL INPUT'; |
||
101 | } else { |
||
102 | $temp[] = 'CALLED ON NULL INPUT'; |
||
103 | } |
||
104 | |||
105 | // Security |
||
106 | $f['prosecdef'] = $this->phpBool($f['prosecdef']); |
||
107 | |||
108 | if ($f['prosecdef']) { |
||
109 | $temp[] = 'SECURITY DEFINER'; |
||
110 | } else { |
||
111 | $temp[] = 'SECURITY INVOKER'; |
||
112 | } |
||
113 | |||
114 | return $temp; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Updates (replaces) a function. |
||
119 | * |
||
120 | * @param string $funcname The name of the function to create |
||
121 | * @param string $newname The new name for the function |
||
122 | * @param string $args imploded array of argument types |
||
123 | * @param string $returns The return type |
||
124 | * @param string $definition The definition for the new function |
||
125 | * @param string $language The language the function is written for |
||
126 | * @param array $flags An array of optional flags |
||
127 | * @param bool $setof True if returns a set, false otherwise |
||
128 | * @param string $funcown |
||
129 | * @param string $newown |
||
130 | * @param string $funcschema |
||
131 | * @param string $newschema |
||
132 | * @param float $cost |
||
133 | * @param int $rows |
||
134 | * @param string $comment The comment on the function |
||
135 | * |
||
136 | * @return bool|int 0 success |
||
137 | */ |
||
138 | public function setFunction( |
||
139 | $funcname, |
||
140 | $newname, |
||
141 | $args, |
||
142 | $returns, |
||
143 | $definition, |
||
144 | $language, |
||
145 | $flags, |
||
146 | $setof, |
||
147 | $funcown, |
||
148 | $newown, |
||
149 | $funcschema, |
||
150 | $newschema, |
||
151 | $cost, |
||
152 | $rows, |
||
153 | $comment |
||
154 | ) { |
||
155 | // Begin a transaction |
||
156 | $status = $this->beginTransaction(); |
||
157 | |||
158 | if (0 !== $status) { |
||
159 | $this->rollbackTransaction(); |
||
160 | |||
161 | return -1; |
||
162 | } |
||
163 | |||
164 | // Replace the existing function |
||
165 | $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true); |
||
166 | |||
167 | if (0 !== $status) { |
||
168 | $this->rollbackTransaction(); |
||
169 | |||
170 | return $status; |
||
171 | } |
||
172 | |||
173 | $f_schema = $this->_schema; |
||
174 | $this->fieldClean($f_schema); |
||
175 | |||
176 | // Rename the function, if necessary |
||
177 | $this->fieldClean($newname); |
||
178 | /* $funcname is escaped in createFunction */ |
||
179 | if ($funcname !== $newname) { |
||
180 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\""; |
||
181 | $status = $this->execute($sql); |
||
182 | |||
183 | if (0 !== $status) { |
||
184 | $this->rollbackTransaction(); |
||
185 | |||
186 | return -5; |
||
187 | } |
||
188 | |||
189 | $funcname = $newname; |
||
190 | } |
||
191 | |||
192 | // Alter the owner, if necessary |
||
193 | if ($this->hasFunctionAlterOwner()) { |
||
194 | $this->fieldClean($newown); |
||
195 | |||
196 | if ($funcown !== $newown) { |
||
197 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\""; |
||
198 | $status = $this->execute($sql); |
||
199 | |||
200 | if (0 !== $status) { |
||
201 | $this->rollbackTransaction(); |
||
202 | |||
203 | return -6; |
||
204 | } |
||
205 | } |
||
206 | } |
||
207 | |||
208 | // Alter the schema, if necessary |
||
209 | if ($this->hasFunctionAlterSchema()) { |
||
210 | $this->fieldClean($newschema); |
||
211 | /* $funcschema is escaped in createFunction */ |
||
212 | if ($funcschema !== $newschema) { |
||
213 | $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\""; |
||
214 | $status = $this->execute($sql); |
||
215 | |||
216 | if (0 !== $status) { |
||
217 | $this->rollbackTransaction(); |
||
218 | |||
219 | return -7; |
||
220 | } |
||
221 | } |
||
222 | } |
||
223 | |||
224 | return $this->endTransaction(); |
||
225 | } |
||
226 | |||
227 | /** |
||
228 | * Creates a new function. |
||
229 | * |
||
230 | * @param string $funcname The name of the function to create |
||
231 | * @param string $args A comma separated string of types |
||
232 | * @param string $returns The return type |
||
233 | * @param string $definition The definition for the new function |
||
234 | * @param string $language The language the function is written for |
||
235 | * @param array $flags An array of optional flags |
||
236 | * @param bool $setof True if it returns a set, false otherwise |
||
237 | * @param string $cost cost the planner should use in the function execution step |
||
238 | * @param int $rows number of rows planner should estimate will be returned |
||
239 | * @param string $comment Comment for the function |
||
240 | * @param bool $replace (optional) True if OR REPLACE, false for |
||
241 | * normal |
||
242 | * |
||
243 | * @return bool|int 0 success |
||
244 | */ |
||
245 | public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false) |
||
246 | { |
||
247 | // Begin a transaction |
||
248 | $status = $this->beginTransaction(); |
||
249 | |||
250 | if (0 !== $status) { |
||
251 | $this->rollbackTransaction(); |
||
252 | |||
253 | return -1; |
||
254 | } |
||
255 | |||
256 | $this->fieldClean($funcname); |
||
257 | $this->clean($args); |
||
258 | $this->fieldClean($language); |
||
259 | $this->arrayClean($flags); |
||
260 | $this->clean($cost); |
||
261 | $this->clean($rows); |
||
262 | $f_schema = $this->_schema; |
||
263 | $this->fieldClean($f_schema); |
||
264 | |||
265 | $sql = 'CREATE'; |
||
266 | |||
267 | if ($replace) { |
||
268 | $sql .= ' OR REPLACE'; |
||
269 | } |
||
270 | |||
271 | $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" ("; |
||
272 | |||
273 | if ('' !== $args) { |
||
274 | $sql .= $args; |
||
275 | } |
||
276 | |||
277 | // For some reason, the returns field cannot have quotes... |
||
278 | $sql .= ') RETURNS '; |
||
279 | |||
280 | if ($setof) { |
||
281 | $sql .= 'SETOF '; |
||
282 | } |
||
283 | |||
284 | $sql .= "{$returns} AS "; |
||
285 | |||
286 | if (\is_array($definition)) { |
||
0 ignored issues
–
show
|
|||
287 | $this->arrayClean($definition); |
||
288 | $sql .= "'" . $definition[0] . "'"; |
||
289 | |||
290 | if ($definition[1]) { |
||
291 | $sql .= ",'" . $definition[1] . "'"; |
||
292 | } |
||
293 | } else { |
||
294 | $this->clean($definition); |
||
295 | $sql .= "'" . $definition . "'"; |
||
296 | } |
||
297 | |||
298 | $sql .= " LANGUAGE \"{$language}\""; |
||
299 | |||
300 | // Add costs |
||
301 | if (!empty($cost)) { |
||
302 | $sql .= " COST {$cost}"; |
||
303 | } |
||
304 | |||
305 | if (0 !== $rows) { |
||
306 | $sql .= " ROWS {$rows}"; |
||
307 | } |
||
308 | |||
309 | // Add flags |
||
310 | foreach ($flags as $v) { |
||
311 | // Skip default flags |
||
312 | if ('' === $v) { |
||
313 | continue; |
||
314 | } |
||
315 | |||
316 | $sql .= "\n{$v}"; |
||
317 | } |
||
318 | |||
319 | $status = $this->execute($sql); |
||
320 | |||
321 | if (0 !== $status) { |
||
322 | $this->rollbackTransaction(); |
||
323 | |||
324 | return -3; |
||
325 | } |
||
326 | |||
327 | /* set the comment */ |
||
328 | $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment); |
||
329 | |||
330 | if (0 !== $status) { |
||
331 | $this->rollbackTransaction(); |
||
332 | |||
333 | return -4; |
||
334 | } |
||
335 | |||
336 | return $this->endTransaction(); |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * Drops a function. |
||
341 | * |
||
342 | * @param int $function_oid The OID of the function to drop |
||
343 | * @param bool $cascade True to cascade drop, false to restrict |
||
344 | * |
||
345 | * @return int|\PHPPgAdmin\ADORecordSet |
||
346 | */ |
||
347 | public function dropFunction($function_oid, $cascade) |
||
348 | { |
||
349 | // Function comes in with $object as function OID |
||
350 | $fn = $this->getFunction($function_oid); |
||
351 | $f_schema = $this->_schema; |
||
352 | $this->fieldClean($f_schema); |
||
353 | $this->fieldClean($fn->fields['proname']); |
||
354 | |||
355 | $sql = "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})"; |
||
356 | |||
357 | if ($cascade) { |
||
358 | $sql .= ' CASCADE'; |
||
359 | } |
||
360 | |||
361 | return $this->execute($sql); |
||
362 | } |
||
363 | |||
364 | /** |
||
365 | * Returns all details for a particular function. |
||
366 | * |
||
367 | * @param int $function_oid |
||
368 | * |
||
369 | * @return int|\PHPPgAdmin\ADORecordSet |
||
370 | * |
||
371 | * @internal param string The $func name of the function to retrieve |
||
372 | */ |
||
373 | public function getFunction($function_oid) |
||
374 | { |
||
375 | $this->clean($function_oid); |
||
376 | |||
377 | $sql = " |
||
378 | SELECT |
||
379 | pc.oid AS prooid, proname, |
||
380 | pg_catalog.pg_get_userbyid(proowner) AS proowner, |
||
381 | nspname as proschema, lanname as prolanguage, procost, prorows, |
||
382 | pg_catalog.format_type(prorettype, NULL) as proresult, prosrc, |
||
383 | probin, proretset, proisstrict, provolatile, prosecdef, |
||
384 | pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments, |
||
385 | proargnames AS proargnames, |
||
386 | pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment, |
||
387 | proconfig, |
||
388 | (select array_agg( (select typname from pg_type pt |
||
389 | where pt.oid = p.oid) ) from unnest(proallargtypes) p) |
||
390 | AS proallarguments, |
||
391 | proargmodes |
||
392 | FROM |
||
393 | pg_catalog.pg_proc pc, pg_catalog.pg_language pl, |
||
394 | pg_catalog.pg_namespace pn |
||
395 | WHERE |
||
396 | pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid |
||
397 | AND pc.pronamespace = pn.oid |
||
398 | "; |
||
399 | |||
400 | return $this->selectSet($sql); |
||
401 | } |
||
402 | |||
403 | /** |
||
404 | * Returns plain definition for a particular function. |
||
405 | * |
||
406 | * @param int $function_oid |
||
407 | * |
||
408 | * @return int|\PHPPgAdmin\ADORecordSet |
||
409 | */ |
||
410 | public function getFunctionDef($function_oid) |
||
411 | { |
||
412 | $this->clean($function_oid); |
||
413 | $sql = " |
||
414 | SELECT |
||
415 | f.proname as relname, |
||
416 | n.nspname, |
||
417 | u.usename AS relowner, |
||
418 | pg_catalog.obj_description(f.oid, 'pg_proc') as relcomment, |
||
419 | (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=f.pronamespace) AS tablespace, |
||
420 | pg_get_functiondef(f.oid), |
||
421 | pl.lanname AS prolanguage |
||
422 | FROM pg_catalog.pg_proc f |
||
423 | JOIN pg_catalog.pg_namespace n ON (f.pronamespace = n.oid) |
||
424 | JOIN pg_catalog.pg_language pl ON pl.oid = f.prolang |
||
425 | LEFT JOIN pg_catalog.pg_user u ON u.usesysid=f.proowner |
||
426 | WHERE f.oid='{$function_oid}' |
||
427 | "; |
||
428 | |||
429 | return $this->selectSet($sql); |
||
430 | } |
||
431 | |||
432 | abstract public function fieldClean(&$str); |
||
433 | |||
434 | abstract public function beginTransaction(); |
||
435 | |||
436 | abstract public function rollbackTransaction(); |
||
437 | |||
438 | abstract public function endTransaction(); |
||
439 | |||
440 | abstract public function execute($sql); |
||
441 | |||
442 | abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null); |
||
443 | |||
444 | abstract public function selectSet($sql); |
||
445 | |||
446 | abstract public function clean(&$str); |
||
447 | |||
448 | abstract public function phpBool($parameter); |
||
449 | |||
450 | abstract public function hasFunctionAlterOwner(); |
||
451 | |||
452 | abstract public function hasFunctionAlterSchema(); |
||
453 | |||
454 | abstract public function arrayClean(&$arr); |
||
455 | } |
||
456 |