Passed
Push — develop ( 1d12a6...8bbd9a )
by Felipe
06:35 queued 39s
created

FunctionTrait::getFunctionProperties()   B

Complexity

Conditions 6
Paths 13

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

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