Test Failed
Push — master ( 34dd7c...325a03 )
by Felipe
11:17 queued 06:14
created

FunctionTrait   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 332
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 33
dl 0
loc 332
rs 9.3999
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
F createFunction() 0 86 13
A dropFunction() 0 14 2
C setFunction() 0 81 11
B getFunctionProperties() 0 32 6
B getFunction() 0 28 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 an array containing a function's properties.
17
     *
18
     * @param array $f The array of data for the function
19
     *
20
     * @return array An array containing the properties
21
     */
22
    public function getFunctionProperties($f)
23
    {
24
        $temp = [];
25
26
        // Volatility
27
        if ($f['provolatile'] == 'v') {
28
            $temp[] = 'VOLATILE';
29
        } elseif ($f['provolatile'] == 'i') {
30
            $temp[] = 'IMMUTABLE';
31
        } elseif ($f['provolatile'] == 's') {
32
            $temp[] = 'STABLE';
33
        } else {
34
            return -1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return -1 returns the type integer which is incompatible with the documented return type array.
Loading history...
35
        }
36
37
        // Null handling
38
        $f['proisstrict'] = $this->phpBool($f['proisstrict']);
0 ignored issues
show
Bug introduced by
It seems like phpBool() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

38
        /** @scrutinizer ignore-call */ 
39
        $f['proisstrict'] = $this->phpBool($f['proisstrict']);
Loading history...
39
        if ($f['proisstrict']) {
40
            $temp[] = 'RETURNS NULL ON NULL INPUT';
41
        } else {
42
            $temp[] = 'CALLED ON NULL INPUT';
43
        }
44
45
        // Security
46
        $f['prosecdef'] = $this->phpBool($f['prosecdef']);
47
        if ($f['prosecdef']) {
48
            $temp[] = 'SECURITY DEFINER';
49
        } else {
50
            $temp[] = 'SECURITY INVOKER';
51
        }
52
53
        return $temp;
54
    }
55
56
    /**
57
     * Updates (replaces) a function.
58
     *
59
     * @param string $funcname     The name of the function to create
60
     * @param string $newname      The new name for the function
61
     * @param string  $args        imploded array of argument types
62
     * @param string $returns      The return type
63
     * @param string $definition   The definition for the new function
64
     * @param string $language     The language the function is written for
65
     * @param array  $flags        An array of optional flags
66
     * @param bool   $setof        True if returns a set, false otherwise
67
     * @param string $funcown
68
     * @param string $newown
69
     * @param string $funcschema
70
     * @param string $newschema
71
     * @param float  $cost
72
     * @param int    $rows
73
     * @param string $comment      The comment on the function
74
     *
75
     * @return bool|int 0 success
76
     */
77
    public function setFunction(
78
        $funcname,
79
        $newname,
80
        $args,
81
        $returns,
82
        $definition,
83
        $language,
84
        $flags,
85
        $setof,
86
        $funcown,
87
        $newown,
88
        $funcschema,
89
        $newschema,
90
        $cost,
91
        $rows,
92
        $comment
93
    ) {
94
        // Begin a transaction
95
        $status = $this->beginTransaction();
96
        if ($status != 0) {
97
            $this->rollbackTransaction();
98
99
            return -1;
100
        }
101
102
        // Replace the existing function
103
        $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
104
        if ($status != 0) {
105
            $this->rollbackTransaction();
106
107
            return $status;
108
        }
109
110
        $f_schema = $this->_schema;
111
        $this->fieldClean($f_schema);
112
113
        // Rename the function, if necessary
114
        $this->fieldClean($newname);
115
        /* $funcname is escaped in createFunction */
116
        if ($funcname != $newname) {
117
            $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
118
            $status = $this->execute($sql);
119
            if ($status != 0) {
120
                $this->rollbackTransaction();
121
122
                return -5;
123
            }
124
125
            $funcname = $newname;
126
        }
127
128
        // Alter the owner, if necessary
129
        if ($this->hasFunctionAlterOwner()) {
0 ignored issues
show
Bug introduced by
It seems like hasFunctionAlterOwner() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

129
        if ($this->/** @scrutinizer ignore-call */ hasFunctionAlterOwner()) {
Loading history...
130
            $this->fieldClean($newown);
131
            if ($funcown != $newown) {
132
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
133
                $status = $this->execute($sql);
134
                if ($status != 0) {
135
                    $this->rollbackTransaction();
136
137
                    return -6;
138
                }
139
            }
140
        }
141
142
        // Alter the schema, if necessary
143
        if ($this->hasFunctionAlterSchema()) {
0 ignored issues
show
Bug introduced by
It seems like hasFunctionAlterSchema() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

143
        if ($this->/** @scrutinizer ignore-call */ hasFunctionAlterSchema()) {
Loading history...
144
            $this->fieldClean($newschema);
145
            /* $funcschema is escaped in createFunction */
146
            if ($funcschema != $newschema) {
147
                $sql    = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
148
                $status = $this->execute($sql);
149
                if ($status != 0) {
150
                    $this->rollbackTransaction();
151
152
                    return -7;
153
                }
154
            }
155
        }
156
157
        return $this->endTransaction();
158
    }
159
160
    /**
161
     * Creates a new function.
162
     *
163
     * @param string $funcname   The name of the function to create
164
     * @param string $args       A comma separated string of types
165
     * @param string $returns    The return type
166
     * @param string $definition The definition for the new function
167
     * @param string $language   The language the function is written for
168
     * @param array  $flags      An array of optional flags
169
     * @param bool   $setof      True if it returns a set, false otherwise
170
     * @param string $cost       cost the planner should use in the function  execution step
171
     * @param int    $rows       number of rows planner should estimate will be returned
172
     * @param string $comment    Comment for the function
173
     * @param bool   $replace    (optional) True if OR REPLACE, false for
174
     *                           normal
175
     *
176
     * @return bool|int 0 success
177
     */
178
    public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false)
179
    {
180
        // Begin a transaction
181
        $status = $this->beginTransaction();
182
        if ($status != 0) {
183
            $this->rollbackTransaction();
184
185
            return -1;
186
        }
187
188
        $this->fieldClean($funcname);
189
        $this->clean($args);
190
        $this->fieldClean($language);
191
        $this->arrayClean($flags);
0 ignored issues
show
Bug introduced by
It seems like arrayClean() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

191
        $this->/** @scrutinizer ignore-call */ 
192
               arrayClean($flags);
Loading history...
192
        $this->clean($cost);
193
        $this->clean($rows);
194
        $f_schema = $this->_schema;
195
        $this->fieldClean($f_schema);
196
197
        $sql = 'CREATE';
198
        if ($replace) {
199
            $sql .= ' OR REPLACE';
200
        }
201
202
        $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" (";
203
204
        if ($args != '') {
205
            $sql .= $args;
206
        }
207
208
        // For some reason, the returns field cannot have quotes...
209
        $sql .= ') RETURNS ';
210
        if ($setof) {
211
            $sql .= 'SETOF ';
212
        }
213
214
        $sql .= "{$returns} AS ";
215
216
        if (is_array($definition)) {
1 ignored issue
show
introduced by
The condition is_array($definition) is always false.
Loading history...
217
            $this->arrayClean($definition);
218
            $sql .= "'" . $definition[0] . "'";
219
            if ($definition[1]) {
220
                $sql .= ",'" . $definition[1] . "'";
221
            }
222
        } else {
223
            $this->clean($definition);
224
            $sql .= "'" . $definition . "'";
225
        }
226
227
        $sql .= " LANGUAGE \"{$language}\"";
228
229
        // Add costs
230
        if (!empty($cost)) {
231
            $sql .= " COST {$cost}";
232
        }
233
234
        if ($rows != 0) {
235
            $sql .= " ROWS {$rows}";
236
        }
237
238
        // Add flags
239
        foreach ($flags as $v) {
240
            // Skip default flags
241
            if ($v == '') {
242
                continue;
243
            }
244
245
            $sql .= "\n{$v}";
246
        }
247
248
        $status = $this->execute($sql);
249
        if ($status != 0) {
250
            $this->rollbackTransaction();
251
252
            return -3;
253
        }
254
255
        /* set the comment */
256
        $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
257
        if ($status != 0) {
258
            $this->rollbackTransaction();
259
260
            return -4;
261
        }
262
263
        return $this->endTransaction();
264
    }
265
266
    /**
267
     * Drops a function.
268
     *
269
     * @param int  $function_oid The OID of the function to drop
270
     * @param bool $cascade      True to cascade drop, false to restrict
271
     *
272
     * @return int 0 if operation was successful
273
     */
274
    public function dropFunction($function_oid, $cascade)
275
    {
276
        // Function comes in with $object as function OID
277
        $fn       = $this->getFunction($function_oid);
278
        $f_schema = $this->_schema;
279
        $this->fieldClean($f_schema);
280
        $this->fieldClean($fn->fields['proname']);
281
282
        $sql = "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
283
        if ($cascade) {
284
            $sql .= ' CASCADE';
285
        }
286
287
        return $this->execute($sql);
288
    }
289
290
    /**
291
     * Returns all details for a particular function.
292
     *
293
     * @param int $function_oid
294
     *
295
     * @return \PHPPgAdmin\ADORecordSet Function info
296
     *
297
     * @internal param string The $func name of the function to retrieve
298
     */
299
    public function getFunction($function_oid)
300
    {
301
        $this->clean($function_oid);
302
303
        $sql = "
304
            SELECT
305
                pc.oid AS prooid, proname,
306
                pg_catalog.pg_get_userbyid(proowner) AS proowner,
307
                nspname as proschema, lanname as prolanguage, procost, prorows,
308
                pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
309
                probin, proretset, proisstrict, provolatile, prosecdef,
310
                pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
311
                proargnames AS proargnames,
312
                pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
313
                proconfig,
314
                (select array_agg( (select typname from pg_type pt
315
                    where pt.oid = p.oid) ) from unnest(proallargtypes) p)
316
                AS proallarguments,
317
                proargmodes
318
            FROM
319
                pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
320
                pg_catalog.pg_namespace pn
321
            WHERE
322
                pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid
323
                AND pc.pronamespace = pn.oid
324
            ";
325
326
        return $this->selectSet($sql);
327
    }
328
329
    abstract public function fieldClean(&$str);
330
331
    abstract public function beginTransaction();
332
333
    abstract public function rollbackTransaction();
334
335
    abstract public function endTransaction();
336
337
    abstract public function execute($sql);
338
339
    abstract public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null);
340
341
    abstract public function selectSet($sql);
342
343
    abstract public function clean(&$str);
344
}
345