Passed
Push — develop ( ec36d3...fcc268 )
by Felipe
09:00
created

FunctionTrait::getTriggerFunctions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.52
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 \PHPPgAdmin\ADORecordSet All functions
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');
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 ($f['provolatile'] == 'v') {
87
            $temp[] = 'VOLATILE';
88
        } elseif ($f['provolatile'] == 'i') {
89
            $temp[] = 'IMMUTABLE';
90
        } elseif ($f['provolatile'] == 's') {
91
            $temp[] = 'STABLE';
92
        } else {
93
            return -1;
94
        }
95
96
        // Null handling
97
        $f['proisstrict'] = $this->phpBool($f['proisstrict']);
98
        if ($f['proisstrict']) {
99
            $temp[] = 'RETURNS NULL ON NULL INPUT';
100
        } else {
101
            $temp[] = 'CALLED ON NULL INPUT';
102
        }
103
104
        // Security
105
        $f['prosecdef'] = $this->phpBool($f['prosecdef']);
106
        if ($f['prosecdef']) {
107
            $temp[] = 'SECURITY DEFINER';
108
        } else {
109
            $temp[] = 'SECURITY INVOKER';
110
        }
111
112
        return $temp;
113
    }
114
115
    /**
116
     * Updates (replaces) a function.
117
     *
118
     * @param string $funcname   The name of the function to create
119
     * @param string $newname    The new name for the function
120
     * @param string $args       imploded array of argument types
121
     * @param string $returns    The return type
122
     * @param string $definition The definition for the new function
123
     * @param string $language   The language the function is written for
124
     * @param array  $flags      An array of optional flags
125
     * @param bool   $setof      True if returns a set, false otherwise
126
     * @param string $funcown
127
     * @param string $newown
128
     * @param string $funcschema
129
     * @param string $newschema
130
     * @param float  $cost
131
     * @param int    $rows
132
     * @param string $comment    The comment on the function
133
     *
134
     * @return bool|int 0 success
135
     */
136
    public function setFunction(
137
        $funcname,
138
        $newname,
139
        $args,
140
        $returns,
141
        $definition,
142
        $language,
143
        $flags,
144
        $setof,
145
        $funcown,
146
        $newown,
147
        $funcschema,
148
        $newschema,
149
        $cost,
150
        $rows,
151
        $comment
152
    ) {
153
        // Begin a transaction
154
        $status = $this->beginTransaction();
155
        if ($status != 0) {
156
            $this->rollbackTransaction();
157
158
            return -1;
159
        }
160
161
        // Replace the existing function
162
        $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
163
        if ($status != 0) {
164
            $this->rollbackTransaction();
165
166
            return $status;
167
        }
168
169
        $f_schema = $this->_schema;
170
        $this->fieldClean($f_schema);
171
172
        // Rename the function, if necessary
173
        $this->fieldClean($newname);
174
        /* $funcname is escaped in createFunction */
175
        if ($funcname != $newname) {
176
            $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
177
            $status = $this->execute($sql);
178
            if ($status != 0) {
179
                $this->rollbackTransaction();
180
181
                return -5;
182
            }
183
184
            $funcname = $newname;
185
        }
186
187
        // Alter the owner, if necessary
188
        if ($this->hasFunctionAlterOwner()) {
189
            $this->fieldClean($newown);
190
            if ($funcown != $newown) {
191
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
192
                $status = $this->execute($sql);
193
                if ($status != 0) {
194
                    $this->rollbackTransaction();
195
196
                    return -6;
197
                }
198
            }
199
        }
200
201
        // Alter the schema, if necessary
202
        if ($this->hasFunctionAlterSchema()) {
203
            $this->fieldClean($newschema);
204
            /* $funcschema is escaped in createFunction */
205
            if ($funcschema != $newschema) {
206
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
207
                $status = $this->execute($sql);
208
                if ($status != 0) {
209
                    $this->rollbackTransaction();
210
211
                    return -7;
212
                }
213
            }
214
        }
215
216
        return $this->endTransaction();
217
    }
218
219
    /**
220
     * Creates a new function.
221
     *
222
     * @param string $funcname   The name of the function to create
223
     * @param string $args       A comma separated string of types
224
     * @param string $returns    The return type
225
     * @param string $definition The definition for the new function
226
     * @param string $language   The language the function is written for
227
     * @param array  $flags      An array of optional flags
228
     * @param bool   $setof      True if it returns a set, false otherwise
229
     * @param string $cost       cost the planner should use in the function  execution step
230
     * @param int    $rows       number of rows planner should estimate will be returned
231
     * @param string $comment    Comment for the function
232
     * @param bool   $replace    (optional) True if OR REPLACE, false for
233
     *                           normal
234
     *
235
     * @return bool|int 0 success
236
     */
237
    public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false)
238
    {
239
        // Begin a transaction
240
        $status = $this->beginTransaction();
241
        if ($status != 0) {
242
            $this->rollbackTransaction();
243
244
            return -1;
245
        }
246
247
        $this->fieldClean($funcname);
248
        $this->clean($args);
249
        $this->fieldClean($language);
250
        $this->arrayClean($flags);
251
        $this->clean($cost);
252
        $this->clean($rows);
253
        $f_schema = $this->_schema;
254
        $this->fieldClean($f_schema);
255
256
        $sql = 'CREATE';
257
        if ($replace) {
258
            $sql .= ' OR REPLACE';
259
        }
260
261
        $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" (";
262
263
        if ($args != '') {
264
            $sql .= $args;
265
        }
266
267
        // For some reason, the returns field cannot have quotes...
268
        $sql .= ') RETURNS ';
269
        if ($setof) {
270
            $sql .= 'SETOF ';
271
        }
272
273
        $sql .= "{$returns} AS ";
274
275
        if (is_array($definition)) {
0 ignored issues
show
introduced by
The condition is_array($definition) is always false.
Loading history...
276
            $this->arrayClean($definition);
277
            $sql .= "'".$definition[0]."'";
278
            if ($definition[1]) {
279
                $sql .= ",'".$definition[1]."'";
280
            }
281
        } else {
282
            $this->clean($definition);
283
            $sql .= "'".$definition."'";
284
        }
285
286
        $sql .= " LANGUAGE \"{$language}\"";
287
288
        // Add costs
289
        if (!empty($cost)) {
290
            $sql .= " COST {$cost}";
291
        }
292
293
        if ($rows != 0) {
294
            $sql .= " ROWS {$rows}";
295
        }
296
297
        // Add flags
298
        foreach ($flags as $v) {
299
            // Skip default flags
300
            if ($v == '') {
301
                continue;
302
            }
303
304
            $sql .= "\n{$v}";
305
        }
306
307
        $status = $this->execute($sql);
308
        if ($status != 0) {
309
            $this->rollbackTransaction();
310
311
            return -3;
312
        }
313
314
        /* set the comment */
315
        $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
316
        if ($status != 0) {
317
            $this->rollbackTransaction();
318
319
            return -4;
320
        }
321
322
        return $this->endTransaction();
323
    }
324
325
    /**
326
     * Drops a function.
327
     *
328
     * @param int  $function_oid The OID of the function to drop
329
     * @param bool $cascade      True to cascade drop, false to restrict
330
     *
331
     * @return int 0 if operation was successful
332
     */
333
    public function dropFunction($function_oid, $cascade)
334
    {
335
        // Function comes in with $object as function OID
336
        $fn       = $this->getFunction($function_oid);
337
        $f_schema = $this->_schema;
338
        $this->fieldClean($f_schema);
339
        $this->fieldClean($fn->fields['proname']);
340
341
        $sql = "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
342
        if ($cascade) {
343
            $sql .= ' CASCADE';
344
        }
345
346
        return $this->execute($sql);
347
    }
348
349
    /**
350
     * Returns all details for a particular function.
351
     *
352
     * @param int $function_oid
353
     *
354
     * @return \PHPPgAdmin\ADORecordSet Function info
355
     *
356
     * @internal param string The $func name of the function to retrieve
357
     */
358
    public function getFunction($function_oid)
359
    {
360
        $this->clean($function_oid);
361
362
        $sql = "
363
            SELECT
364
                pc.oid AS prooid, proname,
365
                pg_catalog.pg_get_userbyid(proowner) AS proowner,
366
                nspname as proschema, lanname as prolanguage, procost, prorows,
367
                pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
368
                probin, proretset, proisstrict, provolatile, prosecdef,
369
                pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
370
                proargnames AS proargnames,
371
                pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
372
                proconfig,
373
                (select array_agg( (select typname from pg_type pt
374
                    where pt.oid = p.oid) ) from unnest(proallargtypes) p)
375
                AS proallarguments,
376
                proargmodes
377
            FROM
378
                pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
379
                pg_catalog.pg_namespace pn
380
            WHERE
381
                pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid
382
                AND pc.pronamespace = pn.oid
383
            ";
384
385
        return $this->selectSet($sql);
386
    }
387
388
    /**
389
     * Returns plain definition for a particular function.
390
     *
391
     * @param int $function_oid
392
     *
393
     * @return \PHPPgAdmin\ADORecordSet Function definition
394
     */
395
    public function getFunctionDef($function_oid)
396
    {
397
        $this->clean($function_oid);
398
        $sql = "
399
            SELECT
400
                f.proname as relname,
401
                n.nspname,
402
                u.usename AS relowner,
403
                 pg_catalog.obj_description(f.oid, 'pg_proc') as relcomment,
404
                 (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=f.pronamespace) AS tablespace,
405
                pg_get_functiondef(f.oid),
406
                pl.lanname AS prolanguage
407
                FROM pg_catalog.pg_proc f
408
                JOIN pg_catalog.pg_namespace n ON (f.pronamespace = n.oid)
409
                JOIN pg_catalog.pg_language pl ON pl.oid = f.prolang
410
                LEFT JOIN pg_catalog.pg_user u ON u.usesysid=f.proowner
411
                WHERE f.oid='{$function_oid}'
412
        ";
413
414
        return $this->selectSet($sql);
415
    }
416
417
    abstract public function fieldClean(&$str);
418
419
    abstract public function beginTransaction();
420
421
    abstract public function rollbackTransaction();
422
423
    abstract public function endTransaction();
424
425
    abstract public function execute($sql);
426
427
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
428
429
    abstract public function selectSet($sql);
430
431
    abstract public function clean(&$str);
432
433
    abstract public function phpBool($parameter);
434
435
    abstract public function hasFunctionAlterOwner();
436
437
    abstract public function hasFunctionAlterSchema();
438
439
    abstract public function arrayClean(&$arr);
440
}
441