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