Passed
Pull Request — master (#70)
by Felipe
03:51
created

Postgres   F

Complexity

Total Complexity 1152

Size/Duplication

Total Lines 9796
Duplicated Lines 17.57 %

Importance

Changes 0
Metric Value
dl 1721
loc 9796
rs 0.6314
c 0
b 0
f 0
wmc 1152

277 Methods

Rating   Name   Duplication   Size   Complexity  
A escapeBytea() 0 3 1
A dropFunction() 0 14 2
A alterSequenceSchema() 12 12 3
A hasDatabaseCollation() 0 3 1
C createEnumType() 8 44 7
A getStatsTableIO() 10 10 1
A dropUser() 0 7 1
A alterSequenceName() 16 16 4
D getSelectSQL() 0 92 22
A getOperators() 0 21 1
A alterTableOwner() 15 15 3
B setRenameUser() 0 25 5
A alterTableTablespace() 15 15 3
B _alterSequence() 0 58 6
A getType() 0 8 1
C createAggregate() 8 46 7
A analyzeDB() 13 13 2
A _encryptPassword() 0 3 1
A browseQueryCount() 0 3 1
A setView() 0 3 1
A dropGroupMember() 0 8 1
A getSchemas() 19 19 2
B deleteRow() 0 28 6
C getPrivileges() 0 58 13
A arrayClean() 0 11 3
B addPrimaryKey() 25 25 6
A phpBool() 0 5 1
A getSequences() 0 21 2
A hasAggregateSortOp() 0 3 1
A hasTablespaces() 0 3 1
A hasUserRename() 0 3 1
A getFtsMappings() 0 9 1
B alterTable() 24 24 4
A hasSharedComments() 0 3 1
B updateFtsConfiguration() 34 34 5
A hasCreateFieldWithConstraints() 0 3 1
B createView() 0 37 6
B getFtsConfigurationMap() 0 31 1
B getLocks() 25 25 2
A addDomainCheckConstraint() 15 15 2
A renameColumn() 0 11 1
D alterSequenceProps() 0 50 16
D createTableLike() 0 43 10
F createRole() 0 58 15
C editRow() 0 67 14
A fieldClean() 0 9 2
C createIndex() 0 38 7
B vacuumDB() 0 23 5
B dropOperator() 0 27 4
B getFunction() 0 28 1
A getTablespace() 0 9 1
B changeFtsMapping() 0 31 6
A hasReadOnlyQueries() 0 3 1
A getStatsIndexTuples() 10 10 1
A endDump() 0 3 1
B getAutovacuum() 0 24 2
A hasMagicTypes() 0 3 1
A dropTablespace() 0 7 1
B _alterView() 31 31 5
A hasCreateTableLikeWithConstraints() 0 3 1
A dropDatabase() 0 6 1
A alterDatabaseRename() 0 12 2
A getFtsParsers() 0 20 2
A dropRole() 0 7 1
F executeScript() 24 245 53
A nextvalSequence() 0 12 1
F setPrivileges() 0 122 36
A getMemberOf() 0 13 1
A hasAlterDomains() 0 3 1
A getTableChildren() 19 19 1
A getSchemaByName() 0 11 1
D browseQuery() 0 101 24
B updateFtsDictionary() 33 33 5
C getTableDefSuffix() 26 57 10
A clusterIndex() 20 20 3
C getDatabases() 0 56 10
A hasAlterDatabaseOwner() 0 3 1
A alreadyClustered() 17 17 1
A dropFtsConfiguration() 12 12 2
C createDomain() 0 47 11
A getTableParents() 21 21 1
A hasGrantOption() 0 3 1
C alterAggregate() 0 63 9
A dropColumnDefault() 10 10 1
B _alterTable() 0 39 6
A dropTrigger() 13 13 2
F getTriggerDef() 15 115 20
A dropRule() 13 13 2
A getGroup() 0 10 1
A dropSchema() 0 10 2
A getTablespaces() 0 15 3
B getLinkingKeys() 0 81 6
A hasForceReindex() 0 3 1
A setColumnNull() 0 10 2
A hasQueryKill() 0 3 1
A getDatabase() 0 6 1
D formatType() 0 42 9
B getAttributeNames() 0 32 5
B getReferrers() 33 33 2
A dropGroup() 0 7 1
A setColumnDefault() 10 10 1
A hasEnumTypes() 0 3 1
A getViews() 0 13 1
A hasFunctionGUC() 0 3 1
A hasNamedParams() 0 3 1
A addGroupMember() 0 8 1
A getTables() 0 23 2
F createCompositeType() 48 106 21
A getDomains() 0 22 1
A getRole() 0 10 1
B createTablespace() 6 27 6
A alterTableName() 18 18 4
F setRole() 30 118 31
B getTableAutovacuum() 0 49 4
A hasDisableTriggers() 0 3 1
D insertRow() 0 35 10
A getVariables() 0 5 1
C setComment() 0 52 17
A renameAggregate() 0 6 1
A hasAlterDatabase() 0 3 1
A dropConstraint() 13 13 2
B findObject() 141 141 5
C formatValue() 0 48 15
A getIndexes() 0 18 2
A getEnumValues() 0 9 1
A grantRole() 11 11 2
A getStatsTableTuples() 10 10 1
A hasFunctionCosting() 0 3 1
B dropCheckConstraint() 0 52 5
A hasConcurrentIndexBuild() 0 3 1
A hasFTS() 0 3 1
A getFtsDictionaryTemplates() 0 21 1
A getView() 0 15 1
B addUniqueKey() 25 25 6
A hasAlterDatabaseRename() 0 3 1
A getTableAttributes() 0 63 2
A getProcesses() 0 17 2
A hasAlterAggregate() 0 3 1
A getTable() 20 20 1
A hasAlterTableSchema() 0 3 1
A dropView() 12 12 2
A dropColumn() 13 13 2
A getConstraintsWithFields() 54 54 4
A hasPrepare() 0 3 1
C updateSchema() 18 44 7
C saveAutovacuum() 0 48 8
C createSchema() 10 38 7
A hasUserSignals() 0 3 1
B setUser() 0 21 5
A getMaterializedViews() 0 13 1
A setSchema() 0 14 2
A renameRole() 0 8 1
B getTypes() 0 39 4
A getDatabaseComment() 0 7 1
F createTable() 48 158 31
A getDatabaseEncoding() 0 3 1
A changePassword() 9 9 1
C createType() 0 50 7
B setRenameRole() 0 43 5
A getGroups() 0 5 1
A getUser() 0 9 1
B sendSignal() 0 24 5
A enableTrigger() 10 10 1
A disableTrigger() 10 10 1
F alterColumn() 8 108 18
A hasFunctionAlterOwner() 0 3 1
B beginDump() 0 36 5
A createTrigger() 0 14 1
C alterDatabase() 9 37 7
B getCasts() 38 38 2
D printField() 16 62 29
A hasAutovacuum() 0 3 1
A getStatsIndexIO() 11 11 1
A dbBool() 0 9 2
A hasCreateTableLikeWithIndexes() 0 3 1
A hasAlterColumnType() 0 3 1
F createFunction() 10 87 13
B reindex() 0 26 6
A dropIndex() 12 12 2
A advance_1() 0 5 1
A valid_dolquote() 0 4 2
A hasRoles() 0 3 1
A fieldArrayClean() 0 11 3
B alterSequence() 0 39 4
A alterViewName() 17 17 4
B createRule() 0 33 6
A getFtsConfigurations() 0 22 2
A getTriggers() 22 22 1
A hasServerAdminFuncs() 0 3 1
A setRule() 0 3 1
A getMembers() 0 12 1
A getUsers() 0 7 1
A getFtsDictionaries() 0 19 2
A changeAggregateSchema() 9 9 1
B alterDomain() 0 53 7
A getSequence() 0 18 1
A getTrigger() 15 15 1
A getRoles() 0 13 2
A alterTableSchema() 14 14 3
A getChangeUserSQL() 0 5 1
A hasQueryCancel() 0 3 1
A hasAlterSchema() 0 3 1
B getFunctions() 0 39 3
A getHelp() 0 17 4
A getRules() 15 15 1
C createDatabase() 6 47 10
A __construct() 0 5 1
A getPreparedXacts() 0 11 2
B getDomainConstraints() 0 24 1
A renameUser() 0 8 1
A clean() 0 10 2
A alterViewOwner() 15 15 3
A getSearchPath() 0 5 1
A revokeRole() 13 13 2
A browseRow() 0 17 4
B getConstraints() 38 38 1
A getDefaultWithOid() 0 5 1
B getRowIdentifier() 0 50 5
A restartSequence() 9 9 1
A alterSequenceOwner() 15 15 3
A hasCreateTableLike() 0 3 1
A getAggregate() 0 23 1
F getTableDefPrefix() 61 319 53
A resetSequence() 0 19 2
C alterTablespace() 6 43 8
A dropAutovacuum() 0 8 1
A getLanguages() 21 21 3
A changeAggregateOwner() 9 9 1
A getHelpPages() 0 9 3
A hasObjectID() 0 17 2
C setFunction() 0 82 11
A hasVirtualTransactionId() 0 3 1
B getFtsMappingByName() 0 30 1
A dropSequence() 12 12 2
A dumpRelation() 0 12 2
D addColumn() 8 68 13
A setvalSequence() 0 13 1
A getTriggerFunctions() 0 3 1
A getDatabaseOwner() 0 6 1
A alterDatabaseOwner() 0 8 1
A hasAlterSequenceStart() 0 3 1
A hasDomainConstraints() 0 3 1
C createUser() 0 26 7
A getStatsDatabase() 0 7 1
A getFtsDictionaryByName() 0 23 1
C createFtsConfiguration() 21 48 8
A getFtsConfigurationByName() 0 20 1
A dropTable() 12 12 2
A isSuperUser() 0 19 4
A hasAlterSequenceSchema() 0 3 1
A hasFunctionAlterSchema() 0 3 1
A getDomain() 0 23 1
A hasAlterSchemaOwner() 0 3 1
A hasByteaHexDefault() 0 3 1
A alterTrigger() 0 11 1
A alterViewSchema() 14 14 3
B alterView() 23 23 4
A dropFtsDictionary() 13 13 2
A getAggregates() 0 12 1
C setSearchPath() 0 25 7
F _parseACL() 23 122 26
C createSequence() 0 44 7
A createGroup() 0 12 3
A hasRecluster() 0 3 1
A addCheckConstraint() 16 16 2
B getOperator() 0 24 1
C addForeignKey() 0 56 11
A getConversions() 0 18 1
A getOpClasses() 0 20 1
A dropDomainConstraint() 13 13 2
A dropDomain() 12 12 2
B getFunctionProperties() 0 32 6
C createFtsDictionary() 15 75 11
A emptyTable() 0 9 1
A dropAggregate() 13 13 2
A hasPreparedXacts() 0 3 1
A dropType() 12 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Postgres often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Postgres, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PHPPgAdmin\Database;
4
5
/**
6
 * A Class that implements the DB Interface for Postgres
7
 * Note: This Class uses ADODB and returns RecordSets.
8
 *
9
 * $Id: Postgres.php,v 1.320 2008/02/20 20:43:09 ioguix Exp $
10
 */
11
class Postgres extends ADOdbBase
12
{
13
    use \PHPPgAdmin\HelperTrait;
14
15
    public $major_version = 9.6;
16
    // Max object name length
17
    public $_maxNameLen = 63;
18
    // Store the current schema
19
    public $_schema;
20
    // Map of database encoding names to HTTP encoding names.  If a
21
    // database encoding does not appear in this list, then its HTTP
22
    // encoding name is the same as its database encoding name.
23
    public $codemap = [
24
        'BIG5'       => 'BIG5',
25
        'EUC_CN'     => 'GB2312',
26
        'EUC_JP'     => 'EUC-JP',
27
        'EUC_KR'     => 'EUC-KR',
28
        'EUC_TW'     => 'EUC-TW',
29
        'GB18030'    => 'GB18030',
30
        'GBK'        => 'GB2312',
31
        'ISO_8859_5' => 'ISO-8859-5',
32
        'ISO_8859_6' => 'ISO-8859-6',
33
        'ISO_8859_7' => 'ISO-8859-7',
34
        'ISO_8859_8' => 'ISO-8859-8',
35
        'JOHAB'      => 'CP1361',
36
        'KOI8'       => 'KOI8-R',
37
        'LATIN1'     => 'ISO-8859-1',
38
        'LATIN2'     => 'ISO-8859-2',
39
        'LATIN3'     => 'ISO-8859-3',
40
        'LATIN4'     => 'ISO-8859-4',
41
        'LATIN5'     => 'ISO-8859-9',
42
        'LATIN6'     => 'ISO-8859-10',
43
        'LATIN7'     => 'ISO-8859-13',
44
        'LATIN8'     => 'ISO-8859-14',
45
        'LATIN9'     => 'ISO-8859-15',
46
        'LATIN10'    => 'ISO-8859-16',
47
        'SJIS'       => 'SHIFT_JIS',
48
        'SQL_ASCII'  => 'US-ASCII',
49
        'UHC'        => 'WIN949',
50
        'UTF8'       => 'UTF-8',
51
        'WIN866'     => 'CP866',
52
        'WIN874'     => 'CP874',
53
        'WIN1250'    => 'CP1250',
54
        'WIN1251'    => 'CP1251',
55
        'WIN1252'    => 'CP1252',
56
        'WIN1256'    => 'CP1256',
57
        'WIN1258'    => 'CP1258',
58
    ];
59
    public $defaultprops = ['', '', ''];
60
    // Extra "magic" types.  BIGSERIAL was added in PostgreSQL 7.2.
61
    public $extraTypes = ['SERIAL', 'BIGSERIAL'];
62
    // Foreign key stuff.  First element MUST be the default.
63
    public $fkactions = ['NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT'];
64
    public $fkdeferrable = ['NOT DEFERRABLE', 'DEFERRABLE'];
65
    public $fkinitial = ['INITIALLY IMMEDIATE', 'INITIALLY DEFERRED'];
66
    public $fkmatches = ['MATCH SIMPLE', 'MATCH FULL'];
67
    // Function properties
68
    public $funcprops = [
69
        ['', 'VOLATILE', 'IMMUTABLE', 'STABLE'],
70
        ['', 'CALLED ON NULL INPUT', 'RETURNS NULL ON NULL INPUT'],
71
        ['', 'SECURITY INVOKER', 'SECURITY DEFINER'],
72
    ];
73
74
    // Default help URL
75
    public $help_base = null;
76
    // Help sub pages
77
    public $help_page = null;
78
    // Name of id column
79
    public $id = 'oid';
80
81
    // Supported join operations for use with view wizard
82
    public $joinOps = ['INNER JOIN' => 'INNER JOIN', 'LEFT JOIN' => 'LEFT JOIN', 'RIGHT JOIN' => 'RIGHT JOIN', 'FULL JOIN' => 'FULL JOIN'];
83
    // Map of internal language name to syntax highlighting name
84
    public $langmap = [
85
        'sql'       => 'SQL',
86
        'plpgsql'   => 'SQL',
87
        'php'       => 'PHP',
88
        'phpu'      => 'PHP',
89
        'plphp'     => 'PHP',
90
        'plphpu'    => 'PHP',
91
        'perl'      => 'Perl',
92
        'perlu'     => 'Perl',
93
        'plperl'    => 'Perl',
94
        'plperlu'   => 'Perl',
95
        'java'      => 'Java',
96
        'javau'     => 'Java',
97
        'pljava'    => 'Java',
98
        'pljavau'   => 'Java',
99
        'plj'       => 'Java',
100
        'plju'      => 'Java',
101
        'python'    => 'Python',
102
        'pythonu'   => 'Python',
103
        'plpython'  => 'Python',
104
        'plpythonu' => 'Python',
105
        'ruby'      => 'Ruby',
106
        'rubyu'     => 'Ruby',
107
        'plruby'    => 'Ruby',
108
        'plrubyu'   => 'Ruby',
109
    ];
110
    // Predefined size types
111
    public $predefined_size_types = [
112
        'abstime',
113
        'aclitem',
114
        'bigserial',
115
        'boolean',
116
        'bytea',
117
        'cid',
118
        'cidr',
119
        'circle',
120
        'date',
121
        'float4',
122
        'float8',
123
        'gtsvector',
124
        'inet',
125
        'int2',
126
        'int4',
127
        'int8',
128
        'macaddr',
129
        'money',
130
        'oid',
131
        'path',
132
        'polygon',
133
        'refcursor',
134
        'regclass',
135
        'regoper',
136
        'regoperator',
137
        'regproc',
138
        'regprocedure',
139
        'regtype',
140
        'reltime',
141
        'serial',
142
        'smgr',
143
        'text',
144
        'tid',
145
        'tinterval',
146
        'tsquery',
147
        'tsvector',
148
        'varbit',
149
        'void',
150
        'xid',
151
    ];
152
    // List of all legal privileges that can be applied to different types
153
    // of objects.
154
    public $privlist = [
155
        'table'      => ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'],
156
        'view'       => ['SELECT', 'INSERT', 'UPDATE', 'DELETE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'],
157
        'sequence'   => ['SELECT', 'UPDATE', 'ALL PRIVILEGES'],
158
        'database'   => ['CREATE', 'TEMPORARY', 'CONNECT', 'ALL PRIVILEGES'],
159
        'function'   => ['EXECUTE', 'ALL PRIVILEGES'],
160
        'language'   => ['USAGE', 'ALL PRIVILEGES'],
161
        'schema'     => ['CREATE', 'USAGE', 'ALL PRIVILEGES'],
162
        'tablespace' => ['CREATE', 'ALL PRIVILEGES'],
163
        'column'     => ['SELECT', 'INSERT', 'UPDATE', 'REFERENCES', 'ALL PRIVILEGES'],
164
    ];
165
    // List of characters in acl lists and the privileges they
166
    // refer to.
167
    public $privmap = [
168
        'r' => 'SELECT',
169
        'w' => 'UPDATE',
170
        'a' => 'INSERT',
171
        'd' => 'DELETE',
172
        'D' => 'TRUNCATE',
173
        'R' => 'RULE',
174
        'x' => 'REFERENCES',
175
        't' => 'TRIGGER',
176
        'X' => 'EXECUTE',
177
        'U' => 'USAGE',
178
        'C' => 'CREATE',
179
        'T' => 'TEMPORARY',
180
        'c' => 'CONNECT',
181
    ];
182
    // Rule action types
183
    public $rule_events = ['SELECT', 'INSERT', 'UPDATE', 'DELETE'];
184
    // Select operators
185
    public $selectOps = [
186
        '='                   => 'i',
187
        '!='                  => 'i',
188
        '<'                   => 'i',
189
        '>'                   => 'i',
190
        '<='                  => 'i',
191
        '>='                  => 'i',
192
        '<<'                  => 'i',
193
        '>>'                  => 'i',
194
        '<<='                 => 'i',
195
        '>>='                 => 'i',
196
        'LIKE'                => 'i',
197
        'NOT LIKE'            => 'i',
198
        'ILIKE'               => 'i',
199
        'NOT ILIKE'           => 'i',
200
        'SIMILAR TO'          => 'i',
201
        'NOT SIMILAR TO'      => 'i',
202
        '~'                   => 'i',
203
        '!~'                  => 'i',
204
        '~*'                  => 'i',
205
        '!~*'                 => 'i',
206
        'IS NULL'             => 'p',
207
        'IS NOT NULL'         => 'p',
208
        'IN'                  => 'x',
209
        'NOT IN'              => 'x',
210
        '@@'                  => 'i',
211
        '@@@'                 => 'i',
212
        '@>'                  => 'i',
213
        '<@'                  => 'i',
214
        '@@ to_tsquery'       => 't',
215
        '@@@ to_tsquery'      => 't',
216
        '@> to_tsquery'       => 't',
217
        '<@ to_tsquery'       => 't',
218
        '@@ plainto_tsquery'  => 't',
219
        '@@@ plainto_tsquery' => 't',
220
        '@> plainto_tsquery'  => 't',
221
        '<@ plainto_tsquery'  => 't',
222
    ];
223
    // Array of allowed trigger events
224
    public $triggerEvents = [
225
        'INSERT',
226
        'UPDATE',
227
        'DELETE',
228
        'INSERT OR UPDATE',
229
        'INSERT OR DELETE',
230
        'DELETE OR UPDATE',
231
        'INSERT OR DELETE OR UPDATE',
232
    ];
233
    // When to execute the trigger
234
    public $triggerExecTimes = ['BEFORE', 'AFTER'];
235
    // How often to execute the trigger
236
    public $triggerFrequency = ['ROW', 'STATEMENT'];
237
    // Array of allowed type alignments
238
    public $typAligns = ['char', 'int2', 'int4', 'double'];
239
    // The default type alignment
240
    public $typAlignDef = 'int4';
241
    // Default index type
242
    public $typIndexDef = 'BTREE';
243
    // Array of allowed index types
244
    public $typIndexes = ['BTREE', 'RTREE', 'GIST', 'GIN', 'HASH'];
245
    // Array of allowed type storage attributes
246
    public $typStorages = ['plain', 'external', 'extended', 'main'];
247
    // The default type storage
248
    public $typStorageDef = 'plain';
249
250
    public function __construct(&$conn, $conf)
251
    {
252
        //$this->prtrace('major_version :' . $this->major_version);
253
        $this->conn = $conn;
254
        $this->conf = $conf;
255
    }
256
257
    /**
258
     * Fetch a URL (or array of URLs) for a given help page.
259
     *
260
     * @param $help
261
     *
262
     * @return array|null|string
263
     */
264
    public function getHelp($help)
265
    {
266
        $this->getHelpPages();
267
268
        if (isset($this->help_page[$help])) {
269
            if (is_array($this->help_page[$help])) {
270
                $urls = [];
271
                foreach ($this->help_page[$help] as $link) {
272
                    $urls[] = $this->help_base.$link;
273
                }
274
275
                return $urls;
276
            }
277
278
            return $this->help_base.$this->help_page[$help];
279
        } else {
280
            return;
281
        }
282
    }
283
284
    /**
285
     * get help page by instancing the corresponding help class
286
     * if $this->help_page and $this->help_base are set, this function is a noop.
287
     */
288
    public function getHelpPages()
289
    {
290
        if ($this->help_page === null || $this->help_base === null) {
291
            $help_classname = '\PHPPgAdmin\Help\PostgresDoc'.str_replace('.', '', $this->major_version);
292
293
            $help_class = new $help_classname($this->conf, $this->major_version);
294
295
            $this->help_base = $help_class->getHelpBase();
296
            $this->help_page = $help_class->getHelpPage();
297
        }
298
    }
299
300
    // Formatting functions
301
302
    /**
303
     * Outputs the HTML code for a particular field.
304
     *
305
     * @param                               $name   The name to give the field
306
     * @param                               $value  The value of the field.  Note this could be 'numeric(7,2)' sort of thing...
307
     * @param                               $type   The database type of the field
308
     * @param array|\PHPPgAdmin\Database\An $extras An array of attributes name as key and attributes' values as value
309
     */
310
    public function printField($name, $value, $type, $extras = [])
311
    {
312
        $lang = $this->lang;
313
314
        // Determine actions string
315
        $extra_str = '';
316
        foreach ($extras as $k => $v) {
317
            $extra_str .= " {$k}=\"".htmlspecialchars($v).'"';
318
        }
319
320
        switch (substr($type, 0, 9)) {
321
            case 'bool':
322
            case 'boolean':
323
                if ($value !== null && $value == '') {
324
                    $value = null;
325
                } elseif ($value == 'true') {
326
                    $value = 't';
327
                } elseif ($value == 'false') {
328
                    $value = 'f';
329
                }
330
331
                // If value is null, 't' or 'f'...
332
                if ($value === null || $value == 't' || $value == 'f') {
333
                    echo '<select name="', htmlspecialchars($name), "\"{$extra_str}>\n";
334
                    echo '<option value=""', ($value === null) ? ' selected="selected"' : '', "></option>\n";
335
                    echo '<option value="t"', ($value == 't') ? ' selected="selected"' : '', ">{$lang['strtrue']}</option>\n";
336
                    echo '<option value="f"', ($value == 'f') ? ' selected="selected"' : '', ">{$lang['strfalse']}</option>\n";
337
                    echo "</select>\n";
338
                } else {
339
                    echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
340
                }
341
                break;
342
            case 'bytea':
343
            case 'bytea[]':
344
                if (!is_null($value)) {
345
                    $value = $this->escapeBytea($value);
346
                }
347
            case 'text':
348
            case 'text[]':
349
            case 'json':
350
            case 'jsonb':
351
            case 'xml':
352 View Code Duplication
            case 'xml[]':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
353
                $n = substr_count($value, "\n");
354
                $n = $n < 5 ? max(2, $n) : $n;
355
                $n = $n > 20 ? 20 : $n;
356
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"85\"{$extra_str}>\n";
357
                echo htmlspecialchars($value);
358
                echo "</textarea>\n";
359
                break;
360
            case 'character':
361 View Code Duplication
            case 'character[]':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
362
                $n = substr_count($value, "\n");
363
                $n = $n < 5 ? 5 : $n;
364
                $n = $n > 20 ? 20 : $n;
365
                echo '<textarea name="', htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"35\"{$extra_str}>\n";
366
                echo htmlspecialchars($value);
367
                echo "</textarea>\n";
368
                break;
369
            default:
370
                echo '<input name="', htmlspecialchars($name), '" value="', htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
371
                break;
372
        }
373
    }
374
375
    /**
376
     * Escapes bytea data for display on the screen.
377
     *
378
     * @param $data The bytea data
379
     *
380
     * @return Data formatted for on-screen display
381
     */
382
    public function escapeBytea($data)
383
    {
384
        return htmlentities($data, ENT_QUOTES, 'UTF-8');
385
    }
386
387
    /**
388
     * Return all information about a particular database.
389
     *
390
     * @param $database The name of the database to retrieve
391
     *
392
     * @return The database info
393
     */
394
    public function getDatabase($database)
395
    {
396
        $this->clean($database);
397
        $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
398
399
        return $this->selectSet($sql);
400
    }
401
402
    /**
403
     * Cleans (escapes) a string.
404
     *
405
     * @param $str The string to clean, by reference
406
     *
407
     * @return The cleaned string
408
     */
409
    public function clean(&$str)
410
    {
411
        if ($str === null) {
412
            return;
413
        }
414
415
        $str = str_replace("\r\n", "\n", $str);
416
        $str = pg_escape_string($str);
417
418
        return $str;
419
    }
420
421
    /**
422
     * Return all database available on the server.
423
     *
424
     * @param $currentdatabase database name that should be on top of the resultset
425
     *
426
     * @return A list of databases, sorted alphabetically
427
     */
428
    public function getDatabases($currentdatabase = null)
429
    {
430
        $conf = $this->conf;
431
        $server_info = $this->server_info;
432
433
        if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser()) {
434
            $username = $server_info['username'];
435
            $this->clean($username);
436
            $clause = " AND pr.rolname='{$username}'";
437
        } else {
438
            $clause = '';
439
        }
440
        if (isset($server_info['useonlydefaultdb']) && $server_info['useonlydefaultdb']) {
441
            $currentdatabase = $server_info['defaultdb'];
442
            $clause .= " AND pdb.datname = '{$currentdatabase}' ";
443
        }
444
445
        if (isset($server_info['hiddendbs']) && $server_info['hiddendbs']) {
446
            $hiddendbs = $server_info['hiddendbs'];
447
            $not_in = "('".implode("','", $hiddendbs)."')";
448
            $clause .= " AND pdb.datname NOT IN {$not_in} ";
449
        }
450
451
        if ($currentdatabase != null) {
452
            $this->clean($currentdatabase);
453
            $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
454
        } else {
455
            $orderby = 'ORDER BY pdb.datname';
456
        }
457
458
        if (!$conf['show_system']) {
459
            $where = ' AND NOT pdb.datistemplate';
460
        } else {
461
            $where = ' AND pdb.datallowconn';
462
        }
463
464
        $sql = "
465
			SELECT pdb.datname AS datname,
466
                    pr.rolname AS datowner,
467
                    pg_encoding_to_char(encoding) AS datencoding,
468
				    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid AND pd.classoid='pg_database'::regclass) AS datcomment,
469
				    (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace,
470
				CASE WHEN pg_catalog.has_database_privilege(current_user, pdb.oid, 'CONNECT')
471
					THEN pg_catalog.pg_database_size(pdb.oid)
472
					ELSE -1 -- set this magic value, which we will convert to no access later
473
				END as dbsize,
474
                pdb.datcollate,
475
                pdb.datctype
476
			FROM pg_catalog.pg_database pdb
477
            LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
478
			WHERE true
479
				{$where}
480
				{$clause}
481
			{$orderby}";
482
483
        return $this->selectSet($sql);
484
    }
485
486
    /**
487
     * Determines whether or not a user is a super user.
488
     *
489
     * @param \PHPPgAdmin\Database\The|string $username The username of the user
490
     *
491
     * @return true if is a super user, false otherwise
492
     */
493
    public function isSuperUser($username = '')
494
    {
495
        $this->clean($username);
496
497
        if (empty($usename)) {
498
            $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
499
            if ($val !== false) {
500
                return $val == 'on';
501
            }
502
        }
503
504
        $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
505
506
        $usesuper = $this->selectField($sql, 'usesuper');
507
        if ($usesuper == -1) {
508
            return false;
509
        }
510
511
        return $usesuper == 't';
512
    }
513
514
    /**
515
     * Return the database comment of a db from the shared description table.
516
     *
517
     * @param string $database the name of the database to get the comment for
518
     *
519
     * @return recordset of the db comment info
520
     */
521
    public function getDatabaseComment($database)
522
    {
523
        $this->clean($database);
524
        $sql =
525
            "SELECT description FROM pg_catalog.pg_database JOIN pg_catalog.pg_shdescription ON (oid=objoid AND classoid='pg_database'::regclass) WHERE pg_database.datname = '{$database}' ";
526
527
        return $this->selectSet($sql);
528
    }
529
530
    /**
531
     * Return the database owner of a db.
532
     *
533
     * @param string $database the name of the database to get the owner for
534
     *
535
     * @return recordset of the db owner info
536
     */
537
    public function getDatabaseOwner($database)
538
    {
539
        $this->clean($database);
540
        $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
541
542
        return $this->selectSet($sql);
543
    }
544
545
    // Help functions
546
547
    // Database functions
548
549
    /**
550
     * Returns the current database encoding.
551
     *
552
     * @return The encoding.  eg. SQL_ASCII, UTF-8, etc.
553
     */
554
    public function getDatabaseEncoding()
555
    {
556
        return pg_parameter_status($this->conn->_connectionID, 'server_encoding');
557
    }
558
559
    /**
560
     * Returns the current default_with_oids setting.
561
     *
562
     * @return default_with_oids setting
563
     */
564
    public function getDefaultWithOid()
565
    {
566
        $sql = 'SHOW default_with_oids';
567
568
        return $this->selectField($sql, 'default_with_oids');
569
    }
570
571
    /**
572
     * Creates a database.
573
     *
574
     * @param        $database   The name of the database to create
575
     * @param        $encoding   Encoding of the database
576
     * @param string $tablespace (optional) The tablespace name
577
     * @param string $comment
578
     * @param string $template
579
     * @param string $lc_collate
580
     * @param string $lc_ctype
581
     *
582
     * @return int 0 success
583
     */
584
    public function createDatabase(
585
        $database,
586
        $encoding,
587
        $tablespace = '',
588
        $comment = '',
589
        $template = 'template1',
590
        $lc_collate = '',
591
        $lc_ctype = ''
592
    ) {
593
        $this->fieldClean($database);
594
        $this->clean($encoding);
595
        $this->fieldClean($tablespace);
596
        $this->fieldClean($template);
597
        $this->clean($lc_collate);
598
        $this->clean($lc_ctype);
599
600
        $sql = "CREATE DATABASE \"{$database}\" WITH TEMPLATE=\"{$template}\"";
601
602
        if ($encoding != '') {
603
            $sql .= " ENCODING='{$encoding}'";
604
        }
605
606
        if ($lc_collate != '') {
607
            $sql .= " LC_COLLATE='{$lc_collate}'";
608
        }
609
610
        if ($lc_ctype != '') {
611
            $sql .= " LC_CTYPE='{$lc_ctype}'";
612
        }
613
614
        if ($tablespace != '' && $this->hasTablespaces()) {
615
            $sql .= " TABLESPACE \"{$tablespace}\"";
616
        }
617
618
        $status = $this->execute($sql);
619
        if ($status != 0) {
620
            return -1;
621
        }
622
623 View Code Duplication
        if ($comment != '' && $this->hasSharedComments()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
624
            $status = $this->setComment('DATABASE', $database, '', $comment);
625
            if ($status != 0) {
626
                return -2;
627
            }
628
        }
629
630
        return 0;
631
    }
632
633
    /**
634
     * Cleans (escapes) an object name (eg. table, field).
635
     *
636
     * @param $str The string to clean, by reference
637
     *
638
     * @return The cleaned string
639
     */
640
    public function fieldClean(&$str)
641
    {
642
        if ($str === null) {
643
            return;
644
        }
645
646
        $str = str_replace('"', '""', $str);
647
648
        return $str;
649
    }
650
651
    public function hasTablespaces()
652
    {
653
        return true;
654
    }
655
656
    public function hasSharedComments()
657
    {
658
        return true;
659
    }
660
661
    /**
662
     * Sets the comment for an object in the database.
663
     *
664
     * @pre All parameters must already be cleaned
665
     *
666
     * @param      $obj_type One of 'TABLE' | 'COLUMN' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'TYPE' | 'FUNCTION' | 'AGGREGATE'
667
     * @param      $obj_name The name of the object for which to attach a comment.
668
     * @param      $table    Name of table that $obj_name belongs to.  Ignored unless $obj_type is 'TABLE' or 'COLUMN'.
669
     * @param      $comment  The comment to add.
670
     * @param null $basetype
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $basetype is correct as it would always require null to be passed?
Loading history...
671
     *
672
     * @return int|\PHPPgAdmin\Database\A 0 success
673
     */
674
    public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null)
675
    {
676
        $sql = "COMMENT ON {$obj_type} ";
677
        $f_schema = $this->_schema;
678
        $this->fieldClean($f_schema);
679
        $this->clean($comment); // Passing in an already cleaned comment will lead to double escaped data
680
        // So, while counter-intuitive, it is important to not clean comments before
681
        // calling setComment. We will clean it here instead.
682
        /*
683
        $this->fieldClean($table);
684
        $this->fieldClean($obj_name);
685
         */
686
687
        switch ($obj_type) {
688
            case 'TABLE':
689
                $sql .= "\"{$f_schema}\".\"{$table}\" IS ";
690
                break;
691
            case 'COLUMN':
692
                $sql .= "\"{$f_schema}\".\"{$table}\".\"{$obj_name}\" IS ";
693
                break;
694
            case 'SEQUENCE':
695
            case 'VIEW':
696
            case 'TEXT SEARCH CONFIGURATION':
697
            case 'TEXT SEARCH DICTIONARY':
698
            case 'TEXT SEARCH TEMPLATE':
699
            case 'TEXT SEARCH PARSER':
700
            case 'TYPE':
701
                $sql .= "\"{$f_schema}\".";
702
            case 'DATABASE':
703
            case 'ROLE':
704
            case 'SCHEMA':
705
            case 'TABLESPACE':
706
                $sql .= "\"{$obj_name}\" IS ";
707
                break;
708
            case 'FUNCTION':
709
                $sql .= "\"{$f_schema}\".{$obj_name} IS ";
710
                break;
711
            case 'AGGREGATE':
712
                $sql .= "\"{$f_schema}\".\"{$obj_name}\" (\"{$basetype}\") IS ";
713
                break;
714
            default:
715
                // Unknown object type
716
                return -1;
717
        }
718
719
        if ($comment != '') {
720
            $sql .= "'{$comment}';";
721
        } else {
722
            $sql .= 'NULL;';
723
        }
724
725
        return $this->execute($sql);
726
    }
727
728
    /**
729
     * Drops a database.
730
     *
731
     * @param $database The name of the database to drop
732
     *
733
     * @return \PHPPgAdmin\Database\A 0 success
734
     */
735
    public function dropDatabase($database)
736
    {
737
        $this->fieldClean($database);
738
        $sql = "DROP DATABASE \"{$database}\"";
739
740
        return $this->execute($sql);
741
    }
742
743
    /**
744
     * Alters a database
745
     * the multiple return vals are for postgres 8+ which support more functionality in alter database.
746
     *
747
     * @param                                 $dbName   The name of the database
748
     * @param                                 $newName  new name for the database
749
     * @param \PHPPgAdmin\Database\The|string $newOwner The new owner for the database
750
     * @param string                          $comment
751
     *
752
     * @return bool|int 0 success
753
     */
754
    public function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
755
    {
756
        $status = $this->beginTransaction();
757
        if ($status != 0) {
758
            $this->rollbackTransaction();
759
760
            return -1;
761
        }
762
763 View Code Duplication
        if ($dbName != $newName) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
764
            $status = $this->alterDatabaseRename($dbName, $newName);
765
            if ($status != 0) {
766
                $this->rollbackTransaction();
767
768
                return -3;
769
            }
770
            $dbName = $newName;
771
        }
772
773
        if ($newOwner != '') {
774
            $status = $this->alterDatabaseOwner($newName, $newOwner);
775
            if ($status != 0) {
776
                $this->rollbackTransaction();
777
778
                return -2;
779
            }
780
        }
781
782
        $this->fieldClean($dbName);
783
        $status = $this->setComment('DATABASE', $dbName, '', $comment);
784
        if ($status != 0) {
785
            $this->rollbackTransaction();
786
787
            return -4;
788
        }
789
790
        return $this->endTransaction();
791
    }
792
793
    /**
794
     * Renames a database, note that this operation cannot be
795
     * performed on a database that is currently being connected to.
796
     *
797
     * @param string $oldName name of database to rename
798
     * @param string $newName new name of database
799
     *
800
     * @return int 0 on success
801
     */
802
    public function alterDatabaseRename($oldName, $newName)
803
    {
804
        $this->fieldClean($oldName);
805
        $this->fieldClean($newName);
806
807
        if ($oldName != $newName) {
808
            $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\"";
809
810
            return $this->execute($sql);
811
        }
812
813
        return 0;
814
    }
815
816
    /**
817
     * Changes ownership of a database
818
     * This can only be done by a superuser or the owner of the database.
819
     *
820
     * @param string $dbName   database to change ownership of
821
     * @param string $newOwner user that will own the database
822
     *
823
     * @return int 0 on success
824
     */
825
    public function alterDatabaseOwner($dbName, $newOwner)
826
    {
827
        $this->fieldClean($dbName);
828
        $this->fieldClean($newOwner);
829
830
        $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
831
832
        return $this->execute($sql);
833
    }
834
835
    /**
836
     * Returns prepared transactions information.
837
     *
838
     * @param $database (optional) Find only prepared transactions executed in a specific database
839
     *
840
     * @return A recordset
841
     */
842
    public function getPreparedXacts($database = null)
843
    {
844
        if ($database === null) {
845
            $sql = 'SELECT * FROM pg_prepared_xacts';
846
        } else {
847
            $this->clean($database);
848
            $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts
849
				WHERE database='{$database}' ORDER BY owner";
850
        }
851
852
        return $this->selectSet($sql);
853
    }
854
855
    /**
856
     * Searches all system catalogs to find objects that match a certain name.
857
     *
858
     * @param $term   The search term
859
     * @param $filter The object type to restrict to ('' means no restriction)
860
     *
861
     * @return A recordset
862
     */
863 View Code Duplication
    public function findObject($term, $filter)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
864
    {
865
        $conf = $this->conf;
866
867
        /*about escaping:
868
         * SET standard_conforming_string is not available before 8.2
869
         * So we must use PostgreSQL specific notation :/
870
         * E'' notation is not available before 8.1
871
         * $$ is available since 8.0
872
         * Nothing specific from 7.4
873
         */
874
875
        // Escape search term for ILIKE match
876
        $this->clean($term);
877
        $this->clean($filter);
878
        $term = str_replace('_', '\_', $term);
879
        $term = str_replace('%', '\%', $term);
880
881
        // Exclude system relations if necessary
882
        if (!$conf['show_system']) {
883
            // XXX: The mention of information_schema here is in the wrong place, but
884
            // it's the quickest fix to exclude the info schema from 7.4
885
            $where = " AND pn.nspname NOT LIKE \$_PATERN_\$pg\_%\$_PATERN_\$ AND pn.nspname != 'information_schema'";
886
            $lan_where = 'AND pl.lanispl';
887
        } else {
888
            $where = '';
889
            $lan_where = '';
890
        }
891
892
        // Apply outer filter
893
        $sql = '';
894
        if ($filter != '') {
895
            $sql = 'SELECT * FROM (';
896
        }
897
898
        $term = "\$_PATERN_\$%{$term}%\$_PATERN_\$";
899
900
        $sql .= "
901
			SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name
902
				FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE {$term} {$where}
903
			UNION ALL
904
			SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
905
				pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
906
				WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE {$term} {$where}
907
			UNION ALL
908
			SELECT CASE WHEN pc.relkind='r' THEN 'COLUMNTABLE' ELSE 'COLUMNVIEW' END, NULL, pn.nspname, pc.relname, pa.attname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
909
				pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid
910
				AND pa.attname ILIKE {$term} AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
911
			UNION ALL
912
			SELECT 'FUNCTION', pp.oid, pn.nspname, NULL, pp.proname || '(' || pg_catalog.oidvectortypes(pp.proargtypes) || ')' FROM pg_catalog.pg_proc pp, pg_catalog.pg_namespace pn
913
				WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE {$term} {$where}
914
			UNION ALL
915
			SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
916
				pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid
917
				AND pi.indexrelid=pc2.oid
918
				AND NOT EXISTS (
919
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
920
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
921
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
922
				)
923
				AND pc2.relname ILIKE {$term} {$where}
924
			UNION ALL
925
			SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
926
				pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
927
				AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
928
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
929
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
930
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
931
				) END
932
				AND pc2.conname ILIKE {$term} {$where}
933
			UNION ALL
934
			SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
935
				pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
936
				AND pc.conname ILIKE {$term} {$where}
937
			UNION ALL
938
			SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
939
				pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
940
					AND ( pt.tgconstraint = 0 OR NOT EXISTS
941
					(SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
942
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
943
					WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
944
				AND pt.tgname ILIKE {$term} {$where}
945
			UNION ALL
946
			SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
947
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
948
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
949
				WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
950
			UNION ALL
951
			SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
952
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
953
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
954
				WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE {$term} {$where}
955
		";
956
957
        // Add advanced objects if show_advanced is set
958
        if ($conf['show_advanced']) {
959
            $sql .= "
960
				UNION ALL
961
				SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL,
962
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
963
					WHERE pt.typnamespace=pn.oid AND typname ILIKE {$term}
964
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
965
					{$where}
966
			 	UNION ALL
967
				SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn
968
					WHERE po.oprnamespace=pn.oid AND oprname ILIKE {$term} {$where}
969
				UNION ALL
970
				SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
971
					pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE {$term} {$where}
972
				UNION ALL
973
				SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
974
					WHERE lanname ILIKE {$term} {$lan_where}
975
				UNION ALL
976
				SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
977
					LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
978
					WHERE p.proisagg AND p.proname ILIKE {$term} {$where}
979
				UNION ALL
980
				SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
981
					pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
982
					AND po.opcname ILIKE {$term} {$where}
983
			";
984
        } // Otherwise just add domains
985
        else {
986
            $sql .= "
987
				UNION ALL
988
				SELECT 'DOMAIN', pt.oid, pn.nspname, NULL,
989
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
990
					WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE {$term}
991
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
992
					{$where}
993
			";
994
        }
995
996
        if ($filter != '') {
997
            // We use like to make RULE, CONSTRAINT and COLUMN searches work
998
            $sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
999
        }
1000
1001
        $sql .= 'ORDER BY type, schemaname, relname, name';
1002
1003
        return $this->selectSet($sql);
1004
    }
1005
1006
    /**
1007
     * Returns all available variable information.
1008
     *
1009
     * @return A recordset
1010
     */
1011
    public function getVariables()
1012
    {
1013
        $sql = 'SHOW ALL';
1014
1015
        return $this->selectSet($sql);
1016
    }
1017
1018
    // Schema functons
1019
1020
    /**
1021
     * Return all schemas in the current database.
1022
     *
1023
     * @return All schemas, sorted alphabetically
1024
     */
1025 View Code Duplication
    public function getSchemas()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1026
    {
1027
        $conf = $this->conf;
1028
1029
        if (!$conf['show_system']) {
1030
            $where = "WHERE nspname NOT LIKE 'pg@_%' ESCAPE '@' AND nspname != 'information_schema'";
1031
        } else {
1032
            $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
1033
        }
1034
1035
        $sql = "
1036
			SELECT pn.nspname, pu.rolname AS nspowner,
1037
				pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
1038
			FROM pg_catalog.pg_namespace pn
1039
				LEFT JOIN pg_catalog.pg_roles pu ON (pn.nspowner = pu.oid)
1040
			{$where}
1041
			ORDER BY nspname";
1042
1043
        return $this->selectSet($sql);
1044
    }
1045
1046
    /**
1047
     * Sets the current working schema.  Will also set Class variable.
1048
     *
1049
     * @param $schema The the name of the schema to work in
1050
     *
1051
     * @return int|\PHPPgAdmin\Database\A 0 success
1052
     */
1053
    public function setSchema($schema)
1054
    {
1055
        // Get the current schema search path, including 'pg_catalog'.
1056
        $search_path = $this->getSearchPath();
1057
        // Prepend $schema to search path
1058
        array_unshift($search_path, $schema);
1059
        $status = $this->setSearchPath($search_path);
1060
        if ($status == 0) {
1061
            $this->_schema = $schema;
1062
1063
            return 0;
1064
        }
1065
1066
        return $status;
1067
    }
1068
1069
    /**
1070
     * Return the current schema search path.
1071
     *
1072
     * @return array of schema names
1073
     */
1074
    public function getSearchPath()
1075
    {
1076
        $sql = 'SELECT current_schemas(false) AS search_path';
1077
1078
        return $this->phpArray($this->selectField($sql, 'search_path'));
1079
    }
1080
1081
    /**
1082
     * Sets the current schema search path.
1083
     *
1084
     * @param $paths An array of schemas in required search order
1085
     *
1086
     * @return int|\PHPPgAdmin\Database\A 0 success
1087
     */
1088
    public function setSearchPath($paths)
1089
    {
1090
        if (!is_array($paths)) {
1091
            return -1;
1092
        }
1093
1094
        if (count($paths) == 0) {
1095
            return -2;
1096
        } elseif (count($paths) == 1 && $paths[0] == '') {
1097
            // Need to handle empty paths in some cases
1098
            $paths[0] = 'pg_catalog';
1099
        }
1100
1101
        // Loop over all the paths to check that none are empty
1102
        $temp = [];
1103
        foreach ($paths as $schema) {
1104
            if ($schema != '') {
1105
                $temp[] = $schema;
1106
            }
1107
        }
1108
        $this->fieldArrayClean($temp);
1109
1110
        $sql = 'SET SEARCH_PATH TO "'.implode('","', $temp).'"';
1111
1112
        return $this->execute($sql);
1113
    }
1114
1115
    /**
1116
     * Cleans (escapes) an array of field names.
1117
     *
1118
     * @param $arr The array to clean, by reference
1119
     *
1120
     * @return The cleaned array
1121
     */
1122
    public function fieldArrayClean(&$arr)
1123
    {
1124
        foreach ($arr as $k => $v) {
1125
            if ($v === null) {
1126
                continue;
1127
            }
1128
1129
            $arr[$k] = str_replace('"', '""', $v);
1130
        }
1131
1132
        return $arr;
1133
    }
1134
1135
    /**
1136
     * Creates a new schema.
1137
     *
1138
     * @param        $schemaname    The name of the schema to create
1139
     * @param string $authorization (optional) The username to create the schema for.
1140
     * @param string $comment       (optional) If omitted, defaults to nothing
1141
     *
1142
     * @return bool|int 0 success
1143
     */
1144
    public function createSchema($schemaname, $authorization = '', $comment = '')
1145
    {
1146
        $this->fieldClean($schemaname);
1147
        $this->fieldClean($authorization);
1148
1149
        $sql = "CREATE SCHEMA \"{$schemaname}\"";
1150
        if ($authorization != '') {
1151
            $sql .= " AUTHORIZATION \"{$authorization}\"";
1152
        }
1153
1154
        if ($comment != '') {
1155
            $status = $this->beginTransaction();
1156
            if ($status != 0) {
1157
                return -1;
1158
            }
1159
        }
1160
1161
        // Create the new schema
1162
        $status = $this->execute($sql);
1163
        if ($status != 0) {
1164
            $this->rollbackTransaction();
1165
1166
            return -1;
1167
        }
1168
1169
        // Set the comment
1170 View Code Duplication
        if ($comment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1171
            $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
1172
            if ($status != 0) {
1173
                $this->rollbackTransaction();
1174
1175
                return -1;
1176
            }
1177
1178
            return $this->endTransaction();
1179
        }
1180
1181
        return 0;
1182
    }
1183
1184
    /**
1185
     * Updates a schema.
1186
     *
1187
     * @param $schemaname The name of the schema to drop
1188
     * @param $comment    The new comment for this schema
1189
     * @param $name
1190
     * @param $owner      The new owner for this schema
1191
     *
1192
     * @return bool|int 0 success
1193
     */
1194
    public function updateSchema($schemaname, $comment, $name, $owner)
1195
    {
1196
        $this->fieldClean($schemaname);
1197
        $this->fieldClean($name);
1198
        $this->fieldClean($owner);
1199
1200
        $status = $this->beginTransaction();
1201
        if ($status != 0) {
1202
            $this->rollbackTransaction();
1203
1204
            return -1;
1205
        }
1206
1207
        $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
1208
        if ($status != 0) {
1209
            $this->rollbackTransaction();
1210
1211
            return -1;
1212
        }
1213
1214
        $schema_rs = $this->getSchemaByName($schemaname);
1215
        /* Only if the owner change */
1216 View Code Duplication
        if ($schema_rs->fields['ownername'] != $owner) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1217
            $sql = "ALTER SCHEMA \"{$schemaname}\" OWNER TO \"{$owner}\"";
1218
            $status = $this->execute($sql);
1219
            if ($status != 0) {
1220
                $this->rollbackTransaction();
1221
1222
                return -1;
1223
            }
1224
        }
1225
1226
        // Only if the name has changed
1227 View Code Duplication
        if ($name != $schemaname) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1228
            $sql = "ALTER SCHEMA \"{$schemaname}\" RENAME TO \"{$name}\"";
1229
            $status = $this->execute($sql);
1230
            if ($status != 0) {
1231
                $this->rollbackTransaction();
1232
1233
                return -1;
1234
            }
1235
        }
1236
1237
        return $this->endTransaction();
1238
    }
1239
1240
    /**
1241
     * Return all information relating to a schema.
1242
     *
1243
     * @param $schema The name of the schema
1244
     *
1245
     * @return Schema information
1246
     */
1247
    public function getSchemaByName($schema)
1248
    {
1249
        $this->clean($schema);
1250
        $sql = "
1251
			SELECT nspname, nspowner, r.rolname AS ownername, nspacl,
1252
				pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
1253
			FROM pg_catalog.pg_namespace pn
1254
				LEFT JOIN pg_roles as r ON pn.nspowner = r.oid
1255
			WHERE nspname='{$schema}'";
1256
1257
        return $this->selectSet($sql);
1258
    }
1259
1260
    // Table functions
1261
1262
    /**
1263
     * Drops a schema.
1264
     *
1265
     * @param $schemaname The name of the schema to drop
1266
     * @param $cascade    True to cascade drop, false to restrict
1267
     *
1268
     * @return \PHPPgAdmin\Database\A 0 success
1269
     */
1270
    public function dropSchema($schemaname, $cascade)
1271
    {
1272
        $this->fieldClean($schemaname);
1273
1274
        $sql = "DROP SCHEMA \"{$schemaname}\"";
1275
        if ($cascade) {
1276
            $sql .= ' CASCADE';
1277
        }
1278
1279
        return $this->execute($sql);
1280
    }
1281
1282
    /**
1283
     * Return all tables in current database (and schema).
1284
     *
1285
     * @param bool|true $all True to fetch all tables, false for just in current schema
1286
     *
1287
     * @return \PHPPgAdmin\Database\All tables, sorted alphabetically
1288
     */
1289
    public function getTables($all = false)
1290
    {
1291
        $c_schema = $this->_schema;
1292
        $this->clean($c_schema);
1293
        if ($all) {
1294
            // Exclude pg_catalog and information_schema tables
1295
            $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
1296
					FROM pg_catalog.pg_tables
1297
					WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
1298
					ORDER BY schemaname, tablename";
1299
        } else {
1300
            $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
1301
						pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
1302
						reltuples::bigint,
1303
						(SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
1304
					FROM pg_catalog.pg_class c
1305
					LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
1306
					WHERE c.relkind = 'r'
1307
					AND nspname='{$c_schema}'
1308
					ORDER BY c.relname";
1309
        }
1310
1311
        return $this->selectSet($sql);
1312
    }
1313
1314
    /**
1315
     * Finds the names and schemas of parent tables (in order).
1316
     *
1317
     * @param $table The table to find the parents for
1318
     *
1319
     * @return A recordset
1320
     */
1321 View Code Duplication
    public function getTableParents($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1322
    {
1323
        $c_schema = $this->_schema;
1324
        $this->clean($c_schema);
1325
        $this->clean($table);
1326
1327
        $sql = "
1328
			SELECT
1329
				pn.nspname, relname
1330
			FROM
1331
				pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
1332
			WHERE
1333
				pc.oid=pi.inhparent
1334
				AND pc.relnamespace=pn.oid
1335
				AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
1336
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
1337
			ORDER BY
1338
				pi.inhseqno
1339
		";
1340
1341
        return $this->selectSet($sql);
1342
    }
1343
1344
    /**
1345
     * Finds the names and schemas of child tables.
1346
     *
1347
     * @param $table The table to find the children for
1348
     *
1349
     * @return A recordset
1350
     */
1351 View Code Duplication
    public function getTableChildren($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1352
    {
1353
        $c_schema = $this->_schema;
1354
        $this->clean($c_schema);
1355
        $this->clean($table);
1356
1357
        $sql = "
1358
			SELECT
1359
				pn.nspname, relname
1360
			FROM
1361
				pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
1362
			WHERE
1363
				pc.oid=pi.inhrelid
1364
				AND pc.relnamespace=pn.oid
1365
				AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
1366
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$c_schema}'))
1367
		";
1368
1369
        return $this->selectSet($sql);
1370
    }
1371
1372
    /**
1373
     * Returns the SQL definition for the table.
1374
     *
1375
     * @pre MUST be run within a transaction
1376
     *
1377
     * @param           $table The table to define
1378
     * @param bool|true $clean True to issue drop command, false otherwise
1379
     *
1380
     * @return \PHPPgAdmin\Database\A string containing the formatted SQL code
1381
     */
1382
    public function getTableDefPrefix($table, $clean = false)
1383
    {
1384
        // Fetch table
1385
        $t = $this->getTable($table);
1386
        if (!is_object($t) || $t->recordCount() != 1) {
1387
            $this->rollbackTransaction();
1388
1389
            return;
1390
        }
1391
        $this->fieldClean($t->fields['relname']);
1392
        $this->fieldClean($t->fields['nspname']);
1393
1394
        // Fetch attributes
1395
        $atts = $this->getTableAttributes($table);
1396
        if (!is_object($atts)) {
1397
            $this->rollbackTransaction();
1398
1399
            return;
1400
        }
1401
1402
        // Fetch constraints
1403
        $cons = $this->getConstraints($table);
1404
        if (!is_object($cons)) {
1405
            $this->rollbackTransaction();
1406
1407
            return;
1408
        }
1409
1410
        // Output a reconnect command to create the table as the correct user
1411
        $sql = $this->getChangeUserSQL($t->fields['relowner'])."\n\n";
1412
1413
        // Set schema search path
1414
        $sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n";
1415
1416
        // Begin CREATE TABLE definition
1417
        $sql .= "-- Definition\n\n";
1418
        // DROP TABLE must be fully qualified in case a table with the same name exists
1419
        // in pg_catalog.
1420
        if (!$clean) {
1421
            $sql .= '-- ';
1422
        }
1423
1424
        $sql .= 'DROP TABLE ';
1425
        $sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n";
1426
        $sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n";
1427
1428
        // Output all table columns
1429
        $col_comments_sql = ''; // Accumulate comments on columns
1430
        $num = $atts->recordCount() + $cons->recordCount();
1431
        $i = 1;
1432
        while (!$atts->EOF) {
1433
            $this->fieldClean($atts->fields['attname']);
1434
            $sql .= "    \"{$atts->fields['attname']}\"";
1435
            // Dump SERIAL and BIGSERIAL columns correctly
1436
            if ($this->phpBool($atts->fields['attisserial']) &&
1437
                ($atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) {
1438
                if ($atts->fields['type'] == 'integer') {
1439
                    $sql .= ' SERIAL';
1440
                } else {
1441
                    $sql .= ' BIGSERIAL';
1442
                }
1443
            } else {
1444
                $sql .= ' '.$this->formatType($atts->fields['type'], $atts->fields['atttypmod']);
1445
1446
                // Add NOT NULL if necessary
1447
                if ($this->phpBool($atts->fields['attnotnull'])) {
1448
                    $sql .= ' NOT NULL';
1449
                }
1450
1451
                // Add default if necessary
1452
                if ($atts->fields['adsrc'] !== null) {
1453
                    $sql .= " DEFAULT {$atts->fields['adsrc']}";
1454
                }
1455
            }
1456
1457
            // Output comma or not
1458
            if ($i < $num) {
1459
                $sql .= ",\n";
1460
            } else {
1461
                $sql .= "\n";
1462
            }
1463
1464
            // Does this column have a comment?
1465 View Code Duplication
            if ($atts->fields['comment'] !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1466
                $this->clean($atts->fields['comment']);
1467
                $col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\"  IS '{$atts->fields['comment']}';\n";
1468
            }
1469
1470
            $atts->moveNext();
1471
            $i++;
1472
        }
1473
        // Output all table constraints
1474
        while (!$cons->EOF) {
1475
            $this->fieldClean($cons->fields['conname']);
1476
            $sql .= "    CONSTRAINT \"{$cons->fields['conname']}\" ";
1477
            // Nasty hack to support pre-7.4 PostgreSQL
1478
            if ($cons->fields['consrc'] !== null) {
1479
                $sql .= $cons->fields['consrc'];
1480
            } else {
1481
                switch ($cons->fields['contype']) {
1482 View Code Duplication
                    case 'p':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1483
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
1484
                        $sql .= 'PRIMARY KEY ('.implode(',', $keys).')';
1485
                        break;
1486 View Code Duplication
                    case 'u':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1487
                        $keys = $this->getAttributeNames($table, explode(' ', $cons->fields['indkey']));
1488
                        $sql .= 'UNIQUE ('.implode(',', $keys).')';
1489
                        break;
1490
                    default:
1491
                        // Unrecognised constraint
1492
                        $this->rollbackTransaction();
1493
1494
                        return;
1495
                }
1496
            }
1497
1498
            // Output comma or not
1499
            if ($i < $num) {
1500
                $sql .= ",\n";
1501
            } else {
1502
                $sql .= "\n";
1503
            }
1504
1505
            $cons->moveNext();
1506
            $i++;
1507
        }
1508
1509
        $sql .= ')';
1510
1511
        // @@@@ DUMP CLUSTERING INFORMATION
1512
1513
        // Inherits
1514
        /*
1515
         * XXX: This is currently commented out as handling inheritance isn't this simple.
1516
         * You also need to make sure you don't dump inherited columns and defaults, as well
1517
         * as inherited NOT NULL and CHECK constraints.  So for the time being, we just do
1518
         * not claim to support inheritance.
1519
        $parents = $this->getTableParents($table);
1520
        if ($parents->recordCount() > 0) {
1521
        $sql .= " INHERITS (";
1522
        while (!$parents->EOF) {
1523
        $this->fieldClean($parents->fields['relname']);
1524
        // Qualify the parent table if it's in another schema
1525
        if ($parents->fields['schemaname'] != $this->_schema) {
1526
        $this->fieldClean($parents->fields['schemaname']);
1527
        $sql .= "\"{$parents->fields['schemaname']}\".";
1528
        }
1529
        $sql .= "\"{$parents->fields['relname']}\"";
1530
1531
        $parents->moveNext();
1532
        if (!$parents->EOF) $sql .= ', ';
1533
        }
1534
        $sql .= ")";
1535
        }
1536
         */
1537
1538
        // Handle WITHOUT OIDS
1539
        if ($this->hasObjectID($table)) {
1540
            $sql .= ' WITH OIDS';
1541
        } else {
1542
            $sql .= ' WITHOUT OIDS';
1543
        }
1544
1545
        $sql .= ";\n";
1546
1547
        // Column storage and statistics
1548
        $atts->moveFirst();
1549
        $first = true;
1550
        while (!$atts->EOF) {
1551
            $this->fieldClean($atts->fields['attname']);
1552
            // Statistics first
1553
            if ($atts->fields['attstattarget'] >= 0) {
1554
                if ($first) {
1555
                    $sql .= "\n";
1556
                    $first = false;
1557
                }
1558
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n";
1559
            }
1560
            // Then storage
1561
            if ($atts->fields['attstorage'] != $atts->fields['typstorage']) {
1562
                switch ($atts->fields['attstorage']) {
1563
                    case 'p':
1564
                        $storage = 'PLAIN';
1565
                        break;
1566
                    case 'e':
1567
                        $storage = 'EXTERNAL';
1568
                        break;
1569
                    case 'm':
1570
                        $storage = 'MAIN';
1571
                        break;
1572
                    case 'x':
1573
                        $storage = 'EXTENDED';
1574
                        break;
1575
                    default:
1576
                        // Unknown storage type
1577
                        $this->rollbackTransaction();
1578
1579
                        return;
1580
                }
1581
                $sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n";
1582
            }
1583
1584
            $atts->moveNext();
1585
        }
1586
1587
        // Comment
1588 View Code Duplication
        if ($t->fields['relcomment'] !== null) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1589
            $this->clean($t->fields['relcomment']);
1590
            $sql .= "\n-- Comment\n\n";
1591
            $sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n";
1592
        }
1593
1594
        // Add comments on columns, if any
1595
        if ($col_comments_sql != '') {
1596
            $sql .= $col_comments_sql;
1597
        }
1598
1599
        // Privileges
1600
        $privs = $this->getPrivileges($table, 'table');
1601
        if (!is_array($privs)) {
1602
            $this->rollbackTransaction();
1603
1604
            return;
1605
        }
1606
1607
        if (count($privs) > 0) {
1608
            $sql .= "\n-- Privileges\n\n";
1609
            /*
1610
             * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
1611
             * wire-in knowledge about the default public privileges for different
1612
             * kinds of objects.
1613
             */
1614
            $sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n";
1615
            foreach ($privs as $v) {
1616
                // Get non-GRANT OPTION privs
1617
                $nongrant = array_diff($v[2], $v[4]);
1618
1619
                // Skip empty or owner ACEs
1620
                if (count($v[2]) == 0 || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) {
1621
                    continue;
1622
                }
1623
1624
                // Change user if necessary
1625 View Code Duplication
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1626
                    $grantor = $v[3];
1627
                    $this->clean($grantor);
1628
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
1629
                }
1630
1631
                // Output privileges with no GRANT OPTION
1632
                $sql .= 'GRANT '.implode(', ', $nongrant)." ON TABLE \"{$t->fields['relname']}\" TO ";
1633 View Code Duplication
                switch ($v[0]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1634
                    case 'public':
1635
                        $sql .= "PUBLIC;\n";
1636
                        break;
1637
                    case 'user':
1638
                        $this->fieldClean($v[1]);
1639
                        $sql .= "\"{$v[1]}\";\n";
1640
                        break;
1641
                    case 'group':
1642
                        $this->fieldClean($v[1]);
1643
                        $sql .= "GROUP \"{$v[1]}\";\n";
1644
                        break;
1645
                    default:
1646
                        // Unknown privilege type - fail
1647
                        $this->rollbackTransaction();
1648
1649
                        return;
1650
                }
1651
1652
                // Reset user if necessary
1653
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
1654
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
1655
                }
1656
1657
                // Output privileges with GRANT OPTION
1658
1659
                // Skip empty or owner ACEs
1660
                if (!$this->hasGrantOption() || count($v[4]) == 0) {
1661
                    continue;
1662
                }
1663
1664
                // Change user if necessary
1665 View Code Duplication
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1666
                    $grantor = $v[3];
1667
                    $this->clean($grantor);
1668
                    $sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
1669
                }
1670
1671
                $sql .= 'GRANT '.implode(', ', $v[4])." ON \"{$t->fields['relname']}\" TO ";
1672 View Code Duplication
                switch ($v[0]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1673
                    case 'public':
1674
                        $sql .= 'PUBLIC';
1675
                        break;
1676
                    case 'user':
1677
                        $this->fieldClean($v[1]);
1678
                        $sql .= "\"{$v[1]}\"";
1679
                        break;
1680
                    case 'group':
1681
                        $this->fieldClean($v[1]);
1682
                        $sql .= "GROUP \"{$v[1]}\"";
1683
                        break;
1684
                    default:
1685
                        // Unknown privilege type - fail
1686
                        return;
1687
                }
1688
                $sql .= " WITH GRANT OPTION;\n";
1689
1690
                // Reset user if necessary
1691
                if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
1692
                    $sql .= "RESET SESSION AUTHORIZATION;\n";
1693
                }
1694
            }
1695
        }
1696
1697
        // Add a newline to separate data that follows (if any)
1698
        $sql .= "\n";
1699
1700
        return $sql;
1701
    }
1702
1703
    /**
1704
     * Returns table information.
1705
     *
1706
     * @param $table The name of the table
1707
     *
1708
     * @return A recordset
1709
     */
1710 View Code Duplication
    public function getTable($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1711
    {
1712
        $c_schema = $this->_schema;
1713
        $this->clean($c_schema);
1714
        $this->clean($table);
1715
1716
        $sql = "
1717
			SELECT
1718
			  c.relname, n.nspname, u.usename AS relowner,
1719
			  pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
1720
			  (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
1721
			FROM pg_catalog.pg_class c
1722
			     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
1723
			     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
1724
			WHERE c.relkind = 'r'
1725
			      AND n.nspname = '{$c_schema}'
1726
			      AND n.oid = c.relnamespace
1727
			      AND c.relname = '{$table}'";
1728
1729
        return $this->selectSet($sql);
1730
    }
1731
1732
    /**
1733
     * Retrieve the attribute definition of a table.
1734
     *
1735
     * @param $table The name of the table
1736
     * @param $field (optional) The name of a field to return
1737
     *
1738
     * @return All attributes in order
1739
     */
1740
    public function getTableAttributes($table, $field = '')
1741
    {
1742
        $c_schema = $this->_schema;
1743
        $this->clean($c_schema);
1744
        $this->clean($table);
1745
        $this->clean($field);
1746
1747
        if ($field == '') {
1748
            // This query is made much more complex by the addition of the 'attisserial' field.
1749
            // The subquery to get that field checks to see if there is an internally dependent
1750
            // sequence on the field.
1751
            $sql = "
1752
				SELECT
1753
					a.attname, a.attnum,
1754
					pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
1755
					a.atttypmod,
1756
					a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
1757
					a.attstattarget, a.attstorage, t.typstorage,
1758
					(
1759
						SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
1760
						WHERE pd.objid=pc.oid
1761
						AND pd.classid=pc.tableoid
1762
						AND pd.refclassid=pc.tableoid
1763
						AND pd.refobjid=a.attrelid
1764
						AND pd.refobjsubid=a.attnum
1765
						AND pd.deptype='i'
1766
						AND pc.relkind='S'
1767
					) IS NOT NULL AS attisserial,
1768
					pg_catalog.col_description(a.attrelid, a.attnum) AS comment
1769
				FROM
1770
					pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
1771
					ON a.attrelid=adef.adrelid
1772
					AND a.attnum=adef.adnum
1773
					LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
1774
				WHERE
1775
					a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
1776
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
1777
						nspname = '{$c_schema}'))
1778
					AND a.attnum > 0 AND NOT a.attisdropped
1779
				ORDER BY a.attnum";
1780
        } else {
1781
            $sql = "
1782
				SELECT
1783
					a.attname, a.attnum,
1784
					pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
1785
					pg_catalog.format_type(a.atttypid, NULL) as base_type,
1786
					a.atttypmod,
1787
					a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as adsrc,
1788
					a.attstattarget, a.attstorage, t.typstorage,
1789
					pg_catalog.col_description(a.attrelid, a.attnum) AS comment
1790
				FROM
1791
					pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
1792
					ON a.attrelid=adef.adrelid
1793
					AND a.attnum=adef.adnum
1794
					LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
1795
				WHERE
1796
					a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
1797
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
1798
						nspname = '{$c_schema}'))
1799
					AND a.attname = '{$field}'";
1800
        }
1801
1802
        return $this->selectSet($sql);
1803
    }
1804
1805
    /**
1806
     * Returns a list of all constraints on a table.
1807
     *
1808
     * @param $table The table to find rules for
1809
     *
1810
     * @return A recordset
1811
     */
1812 View Code Duplication
    public function getConstraints($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1813
    {
1814
        $c_schema = $this->_schema;
1815
        $this->clean($c_schema);
1816
        $this->clean($table);
1817
1818
        // This SQL is greatly complicated by the need to retrieve
1819
        // index clustering information for primary and unique constraints
1820
        $sql = "SELECT
1821
				pc.conname,
1822
				pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
1823
				pc.contype,
1824
				CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
1825
					SELECT
1826
						indisclustered
1827
					FROM
1828
						pg_catalog.pg_depend pd,
1829
						pg_catalog.pg_class pl,
1830
						pg_catalog.pg_index pi
1831
					WHERE
1832
						pd.refclassid=pc.tableoid
1833
						AND pd.refobjid=pc.oid
1834
						AND pd.objid=pl.oid
1835
						AND pl.oid=pi.indexrelid
1836
				) ELSE
1837
					NULL
1838
				END AS indisclustered
1839
			FROM
1840
				pg_catalog.pg_constraint pc
1841
			WHERE
1842
				pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
1843
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
1844
					WHERE nspname='{$c_schema}'))
1845
			ORDER BY
1846
				1
1847
		";
1848
1849
        return $this->selectSet($sql);
1850
    }
1851
1852
    /**
1853
     * Returns the SQL for changing the current user.
1854
     *
1855
     * @param $user The user to change to
1856
     *
1857
     * @return The SQL
1858
     */
1859
    public function getChangeUserSQL($user)
1860
    {
1861
        $this->clean($user);
1862
1863
        return "SET SESSION AUTHORIZATION '{$user}';";
1864
    }
1865
1866
    /**
1867
     * Change a parameter from 't' or 'f' to a boolean, (others evaluate to false).
1868
     *
1869
     * @param $parameter the parameter
1870
     *
1871
     * @return bool|\PHPPgAdmin\Database\the
1872
     */
1873
    public function phpBool($parameter)
1874
    {
1875
        $parameter = ($parameter == 't');
1876
1877
        return $parameter;
1878
    }
1879
1880
    /**
1881
     * Formats a type correctly for display.  Postgres 7.0 had no 'format_type'
1882
     * built-in function, and hence we need to do it manually.
1883
     *
1884
     * @param $typname The name of the type
1885
     * @param $typmod  The contents of the typmod field
1886
     *
1887
     * @return bool|\PHPPgAdmin\Database\The|string
1888
     */
1889
    public function formatType($typname, $typmod)
1890
    {
1891
        // This is a specific constant in the 7.0 source
1892
        $varhdrsz = 4;
1893
1894
        // If the first character is an underscore, it's an array type
1895
        $is_array = false;
1896
        if (substr($typname, 0, 1) == '_') {
1897
            $is_array = true;
1898
            $typname = substr($typname, 1);
1899
        }
1900
1901
        // Show lengths on bpchar and varchar
1902
        if ($typname == 'bpchar') {
1903
            $len = $typmod - $varhdrsz;
1904
            $temp = 'character';
1905
            if ($len > 1) {
1906
                $temp .= "({$len})";
1907
            }
1908
        } elseif ($typname == 'varchar') {
1909
            $temp = 'character varying';
1910
            if ($typmod != -1) {
1911
                $temp .= '('.($typmod - $varhdrsz).')';
1912
            }
1913
        } elseif ($typname == 'numeric') {
1914
            $temp = 'numeric';
1915
            if ($typmod != -1) {
1916
                $tmp_typmod = $typmod - $varhdrsz;
1917
                $precision = ($tmp_typmod >> 16) & 0xffff;
1918
                $scale = $tmp_typmod & 0xffff;
1919
                $temp .= "({$precision}, {$scale})";
1920
            }
1921
        } else {
1922
            $temp = $typname;
1923
        }
1924
1925
        // Add array qualifier if it's an array
1926
        if ($is_array) {
1927
            $temp .= '[]';
1928
        }
1929
1930
        return $temp;
1931
    }
1932
1933
    /**
1934
     * Given an array of attnums and a relation, returns an array mapping
1935
     * attribute number to attribute name.
1936
     *
1937
     * @param $table The table to get attributes for
1938
     * @param $atts  An array of attribute numbers
1939
     *
1940
     * @return An array mapping attnum to attname
1941
     * @return -1 $atts must be an array
1942
     * @return -2 wrong number of attributes found
1943
     */
1944
    public function getAttributeNames($table, $atts)
1945
    {
1946
        $c_schema = $this->_schema;
1947
        $this->clean($c_schema);
1948
        $this->clean($table);
1949
        $this->arrayClean($atts);
1950
1951
        if (!is_array($atts)) {
1952
            return -1;
1953
        }
1954
1955
        if (count($atts) == 0) {
1956
            return [];
1957
        }
1958
1959
        $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
1960
			attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
1961
			relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
1962
			AND attnum IN ('".implode("','", $atts)."')";
1963
1964
        $rs = $this->selectSet($sql);
1965
        if ($rs->recordCount() != count($atts)) {
1966
            return -2;
1967
        }
1968
1969
        $temp = [];
1970
        while (!$rs->EOF) {
1971
            $temp[$rs->fields['attnum']] = $rs->fields['attname'];
1972
            $rs->moveNext();
1973
        }
1974
1975
        return $temp;
1976
    }
1977
1978
    /**
1979
     * Cleans (escapes) an array.
1980
     *
1981
     * @param $arr The array to clean, by reference
1982
     *
1983
     * @return The cleaned array
1984
     */
1985
    public function arrayClean(&$arr)
1986
    {
1987
        foreach ($arr as $k => $v) {
1988
            if ($v === null) {
1989
                continue;
1990
            }
1991
1992
            $arr[$k] = pg_escape_string($v);
1993
        }
1994
1995
        return $arr;
1996
    }
1997
1998
    /**
1999
     * Checks to see whether or not a table has a unique id column.
2000
     *
2001
     * @param $table The table name
2002
     *
2003
     * @return true if it has a unique id, false otherwise
2004
     * @return null error
2005
     **/
2006
    public function hasObjectID($table)
2007
    {
2008
        $c_schema = $this->_schema;
2009
        $this->clean($c_schema);
2010
        $this->clean($table);
2011
2012
        $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}'
2013
			AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')";
2014
2015
        $rs = $this->selectSet($sql);
2016
        if ($rs->recordCount() != 1) {
2017
            return;
2018
        }
2019
2020
        $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
2021
2022
        return $rs->fields['relhasoids'];
2023
    }
2024
2025
    /**
2026
     * Grabs an array of users and their privileges for an object,
2027
     * given its type.
2028
     *
2029
     * @param $object The name of the object whose privileges are to be retrieved
2030
     * @param $type   The type of the object (eg. database, schema, relation, function or language)
2031
     * @param $table  Optional, column's table if type = column
2032
     *
2033
     * @return Privileges array
2034
     * @return -1         invalid type
2035
     * @return -2         object not found
2036
     * @return -3         unknown privilege type
2037
     */
2038
    public function getPrivileges($object, $type, $table = null)
2039
    {
2040
        $c_schema = $this->_schema;
2041
        $this->clean($c_schema);
2042
        $this->clean($object);
2043
2044
        switch ($type) {
2045
            case 'column':
2046
                $this->clean($table);
2047
                $sql = "
2048
					SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
2049
					FROM pg_catalog.pg_attribute a
2050
						LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
2051
						LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
2052
					WHERE n.nspname='{$c_schema}'
2053
						AND c.relname='{$table}'
2054
						AND a.attname='{$object}'";
2055
                break;
2056
            case 'table':
2057
            case 'view':
2058
            case 'sequence':
2059
                $sql = "
2060
					SELECT relacl AS acl FROM pg_catalog.pg_class
2061
					WHERE relname='{$object}'
2062
						AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
2063
							WHERE nspname='{$c_schema}')";
2064
                break;
2065
            case 'database':
2066
                $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
2067
                break;
2068
            case 'function':
2069
                // Since we fetch functions by oid, they are already constrained to
2070
                // the current schema.
2071
                $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
2072
                break;
2073
            case 'language':
2074
                $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
2075
                break;
2076
            case 'schema':
2077
                $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
2078
                break;
2079
            case 'tablespace':
2080
                $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
2081
                break;
2082
            default:
2083
                return -1;
2084
        }
2085
2086
        // Fetch the ACL for object
2087
        $acl = $this->selectField($sql, 'acl');
2088
        if ($acl == -1) {
2089
            return -2;
2090
        }
2091
2092
        if ($acl == '' || $acl == null) {
2093
            return [];
2094
        } else {
2095
            return $this->_parseACL($acl);
2096
        }
2097
    }
2098
2099
    /**
2100
     * Internal function used for parsing ACLs.
2101
     *
2102
     * @param $acl The ACL to parse (of type aclitem[])
2103
     *
2104
     * @return Privileges array
2105
     */
2106
    public function _parseACL($acl)
2107
    {
2108
        // Take off the first and last characters (the braces)
2109
        $acl = substr($acl, 1, strlen($acl) - 2);
2110
2111
        // Pick out individual ACE's by carefully parsing.  This is necessary in order
2112
        // to cope with usernames and stuff that contain commas
2113
        $aces = [];
2114
        $i = $j = 0;
2115
        $in_quotes = false;
2116 View Code Duplication
        while ($i < strlen($acl)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2117
            // If current char is a double quote and it's not escaped, then
2118
            // enter quoted bit
2119
            $char = substr($acl, $i, 1);
2120
            if ($char == '"' && ($i == 0 || substr($acl, $i - 1, 1) != '\\')) {
2121
                $in_quotes = !$in_quotes;
2122
            } elseif ($char == ',' && !$in_quotes) {
2123
                // Add text so far to the array
2124
                $aces[] = substr($acl, $j, $i - $j);
2125
                $j = $i + 1;
2126
            }
2127
            $i++;
2128
        }
2129
        // Add final text to the array
2130
        $aces[] = substr($acl, $j);
2131
2132
        // Create the array to be returned
2133
        $temp = [];
2134
2135
        // For each ACE, generate an entry in $temp
2136
        foreach ($aces as $v) {
2137
2138
            // If the ACE begins with a double quote, strip them off both ends
2139
            // and unescape backslashes and double quotes
2140
            $unquote = false;
2141
            if (strpos($v, '"') === 0) {
2142
                $v = substr($v, 1, strlen($v) - 2);
2143
                $v = str_replace('\\"', '"', $v);
2144
                $v = str_replace('\\\\', '\\', $v);
2145
            }
2146
2147
            // Figure out type of ACE (public, user or group)
2148
            if (strpos($v, '=') === 0) {
2149
                $atype = 'public';
2150
            } else {
2151
                if ($this->hasRoles()) {
2152
                    $atype = 'role';
2153
                } else {
2154
                    if (strpos($v, 'group ') === 0) {
2155
                        $atype = 'group';
2156
                        // Tear off 'group' prefix
2157
                        $v = substr($v, 6);
2158
                    } else {
2159
                        $atype = 'user';
2160
                    }
2161
                }
2162
            }
2163
2164
            // Break on unquoted equals sign...
2165
            $i = 0;
2166
            $in_quotes = false;
2167
            $entity = null;
2168
            $chars = null;
2169
            while ($i < strlen($v)) {
2170
                // If current char is a double quote and it's not escaped, then
2171
                // enter quoted bit
2172
                $char = substr($v, $i, 1);
2173
                $next_char = substr($v, $i + 1, 1);
2174
                if ($char == '"' && ($i == 0 || $next_char != '"')) {
2175
                    $in_quotes = !$in_quotes;
2176
                } // Skip over escaped double quotes
2177
                elseif ($char == '"' && $next_char == '"') {
2178
                    $i++;
2179
                } elseif ($char == '=' && !$in_quotes) {
2180
                    // Split on current equals sign
2181
                    $entity = substr($v, 0, $i);
2182
                    $chars = substr($v, $i + 1);
2183
                    break;
2184
                }
2185
                $i++;
2186
            }
2187
2188
            // Check for quoting on entity name, and unescape if necessary
2189
            if (strpos($entity, '"') === 0) {
2190
                $entity = substr($entity, 1, strlen($entity) - 2);
2191
                $entity = str_replace('""', '"', $entity);
2192
            }
2193
2194
            // New row to be added to $temp
2195
            // (type, grantee, privileges, grantor, grant option?
2196
            $row = [$atype, $entity, [], '', []];
2197
2198
            // Loop over chars and add privs to $row
2199
            for ($i = 0; $i < strlen($chars); $i++) {
2200
                // Append to row's privs list the string representing
2201
                // the privilege
2202
                $char = substr($chars, $i, 1);
2203
                if ($char == '*') {
2204
                    $row[4][] = $this->privmap[substr($chars, $i - 1, 1)];
2205 View Code Duplication
                } elseif ($char == '/') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2206
                    $grantor = substr($chars, $i + 1);
2207
                    // Check for quoting
2208
                    if (strpos($grantor, '"') === 0) {
2209
                        $grantor = substr($grantor, 1, strlen($grantor) - 2);
2210
                        $grantor = str_replace('""', '"', $grantor);
2211
                    }
2212
                    $row[3] = $grantor;
2213
                    break;
2214
                } else {
2215
                    if (!isset($this->privmap[$char])) {
2216
                        return -3;
2217
                    }
2218
2219
                    $row[2][] = $this->privmap[$char];
2220
                }
2221
            }
2222
2223
            // Append row to temp
2224
            $temp[] = $row;
2225
        }
2226
2227
        return $temp;
2228
    }
2229
2230
    public function hasRoles()
2231
    {
2232
        return true;
2233
    }
2234
2235
    public function hasGrantOption()
2236
    {
2237
        return true;
2238
    }
2239
2240
    /**
2241
     * Returns extra table definition information that is most usefully
2242
     * dumped after the table contents for speed and efficiency reasons.
2243
     *
2244
     * @param $table The table to define
2245
     *
2246
     * @return A    string containing the formatted SQL code
2247
     * @return null On error
2248
     */
2249
    public function getTableDefSuffix($table)
2250
    {
2251
        $sql = '';
2252
2253
        // Indexes
2254
        $indexes = $this->getIndexes($table);
2255
        if (!is_object($indexes)) {
2256
            $this->rollbackTransaction();
2257
2258
            return;
2259
        }
2260
2261 View Code Duplication
        if ($indexes->recordCount() > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2262
            $sql .= "\n-- Indexes\n\n";
2263
            while (!$indexes->EOF) {
2264
                $sql .= $indexes->fields['inddef'].";\n";
2265
2266
                $indexes->moveNext();
2267
            }
2268
        }
2269
2270
        // Triggers
2271
        $triggers = $this->getTriggers($table);
2272
        if (!is_object($triggers)) {
2273
            $this->rollbackTransaction();
2274
2275
            return;
2276
        }
2277
2278 View Code Duplication
        if ($triggers->recordCount() > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2279
            $sql .= "\n-- Triggers\n\n";
2280
            while (!$triggers->EOF) {
2281
                $sql .= $triggers->fields['tgdef'];
2282
                $sql .= ";\n";
2283
2284
                $triggers->moveNext();
2285
            }
2286
        }
2287
2288
        // Rules
2289
        $rules = $this->getRules($table);
2290
        if (!is_object($rules)) {
2291
            $this->rollbackTransaction();
2292
2293
            return;
2294
        }
2295
2296 View Code Duplication
        if ($rules->recordCount() > 0) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2297
            $sql .= "\n-- Rules\n\n";
2298
            while (!$rules->EOF) {
2299
                $sql .= $rules->fields['definition']."\n";
2300
2301
                $rules->moveNext();
2302
            }
2303
        }
2304
2305
        return $sql;
2306
    }
2307
2308
    /**
2309
     * Grabs a list of indexes for a table.
2310
     *
2311
     * @param \PHPPgAdmin\Database\The|string $table  The name of a table whose indexes to retrieve
2312
     * @param bool|\PHPPgAdmin\Database\Only  $unique Only get unique/pk indexes
2313
     *
2314
     * @return \PHPPgAdmin\Database\A recordset
2315
     */
2316
    public function getIndexes($table = '', $unique = false)
2317
    {
2318
        $this->clean($table);
2319
2320
        $sql = "
2321
			SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
2322
				pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
2323
			FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
2324
			WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
2325
				AND c.oid = i.indrelid AND i.indexrelid = c2.oid
2326
		";
2327
        if ($unique) {
2328
            $sql .= ' AND i.indisunique ';
2329
        }
2330
2331
        $sql .= ' ORDER BY c2.relname';
2332
2333
        return $this->selectSet($sql);
2334
    }
2335
2336
    /**
2337
     * Grabs a list of triggers on a table.
2338
     *
2339
     * @param \PHPPgAdmin\Database\The|string $table The name of a table whose triggers to retrieve
2340
     *
2341
     * @return \PHPPgAdmin\Database\A recordset
2342
     */
2343 View Code Duplication
    public function getTriggers($table = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2344
    {
2345
        $c_schema = $this->_schema;
2346
        $this->clean($c_schema);
2347
        $this->clean($table);
2348
2349
        $sql = "SELECT
2350
				t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
2351
				CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
2352
				p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
2353
				ns.nspname AS pronamespace
2354
			FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
2355
			WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
2356
				AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}'))
2357
				AND ( tgconstraint = 0 OR NOT EXISTS
2358
						(SELECT 1 FROM pg_catalog.pg_depend d    JOIN pg_catalog.pg_constraint c
2359
							ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
2360
						WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
2361
				AND p.oid=t.tgfoid
2362
				AND p.pronamespace = ns.oid";
2363
2364
        return $this->selectSet($sql);
2365
    }
2366
2367
    /**
2368
     * Returns a list of all rules on a table OR view.
2369
     *
2370
     * @param $table The table to find rules for
2371
     *
2372
     * @return A recordset
2373
     */
2374 View Code Duplication
    public function getRules($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2375
    {
2376
        $c_schema = $this->_schema;
2377
        $this->clean($c_schema);
2378
        $this->clean($table);
2379
2380
        $sql = "
2381
			SELECT *
2382
			FROM pg_catalog.pg_rules
2383
			WHERE
2384
				schemaname='{$c_schema}' AND tablename='{$table}'
2385
			ORDER BY rulename
2386
		";
2387
2388
        return $this->selectSet($sql);
2389
    }
2390
2391
    /**
2392
     * Creates a new table in the database.
2393
     *
2394
     * @param $name        The name of the table
2395
     * @param $fields      The number of fields
2396
     * @param $field       An array of field names
2397
     * @param $type        An array of field types
2398
     * @param $array       An array of '' or '[]' for each type if it's an array or not
2399
     * @param $length      An array of field lengths
2400
     * @param $notnull     An array of not null
2401
     * @param $default     An array of default values
2402
     * @param $withoutoids True if WITHOUT OIDS, false otherwise
2403
     * @param $colcomment  An array of comments
2404
     * @param $tblcomment
2405
     * @param $tablespace  The tablespace name ('' means none/default)
2406
     * @param $uniquekey   An Array indicating the fields that are unique (those indexes that are set)
2407
     * @param $primarykey  An Array indicating the field used for the primarykey (those indexes that are set)
2408
     *
2409
     * @return bool|int 0 success
2410
     *
2411
     * @internal param \PHPPgAdmin\Database\Table $comment comment
2412
     */
2413
    public function createTable(
2414
        $name,
2415
        $fields,
2416
        $field,
2417
        $type,
2418
        $array,
2419
        $length,
2420
        $notnull,
2421
        $default,
2422
        $withoutoids,
2423
        $colcomment,
2424
        $tblcomment,
2425
        $tablespace,
2426
        $uniquekey,
2427
        $primarykey
2428
    ) {
2429
        $f_schema = $this->_schema;
2430
        $this->fieldClean($f_schema);
2431
        $this->fieldClean($name);
2432
2433
        $status = $this->beginTransaction();
2434
        if ($status != 0) {
2435
            return -1;
2436
        }
2437
2438
        $found = false;
2439
        $first = true;
2440
        $comment_sql = ''; //Accumulate comments for the columns
2441
        $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (";
2442
        for ($i = 0; $i < $fields; $i++) {
2443
            $this->fieldClean($field[$i]);
2444
            $this->clean($type[$i]);
2445
            $this->clean($length[$i]);
2446
            $this->clean($colcomment[$i]);
2447
2448
            // Skip blank columns - for user convenience
2449
            if ($field[$i] == '' || $type[$i] == '') {
2450
                continue;
2451
            }
2452
2453
            // If not the first column, add a comma
2454
            if (!$first) {
2455
                $sql .= ', ';
2456
            } else {
2457
                $first = false;
2458
            }
2459
2460 View Code Duplication
            switch ($type[$i]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2461
                // Have to account for weird placing of length for with/without
2462
                // time zone types
2463
                case 'timestamp with time zone':
2464
                case 'timestamp without time zone':
2465
                    $qual = substr($type[$i], 9);
2466
                    $sql .= "\"{$field[$i]}\" timestamp";
2467
                    if ($length[$i] != '') {
2468
                        $sql .= "({$length[$i]})";
2469
                    }
2470
2471
                    $sql .= $qual;
2472
                    break;
2473
                case 'time with time zone':
2474
                case 'time without time zone':
2475
                    $qual = substr($type[$i], 4);
2476
                    $sql .= "\"{$field[$i]}\" time";
2477
                    if ($length[$i] != '') {
2478
                        $sql .= "({$length[$i]})";
2479
                    }
2480
2481
                    $sql .= $qual;
2482
                    break;
2483
                default:
2484
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
2485
                    if ($length[$i] != '') {
2486
                        $sql .= "({$length[$i]})";
2487
                    }
2488
            }
2489
            // Add array qualifier if necessary
2490
            if ($array[$i] == '[]') {
2491
                $sql .= '[]';
2492
            }
2493
2494
            // Add other qualifiers
2495
            if (!isset($primarykey[$i])) {
2496
                if (isset($uniquekey[$i])) {
2497
                    $sql .= ' UNIQUE';
2498
                }
2499
2500
                if (isset($notnull[$i])) {
2501
                    $sql .= ' NOT NULL';
2502
                }
2503
            }
2504
            if ($default[$i] != '') {
2505
                $sql .= " DEFAULT {$default[$i]}";
2506
            }
2507
2508 View Code Duplication
            if ($colcomment[$i] != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2509
                $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
2510
            }
2511
2512
            $found = true;
2513
        }
2514
2515
        if (!$found) {
2516
            return -1;
2517
        }
2518
2519
        // PRIMARY KEY
2520
        $primarykeycolumns = [];
2521
        for ($i = 0; $i < $fields; $i++) {
2522
            if (isset($primarykey[$i])) {
2523
                $primarykeycolumns[] = "\"{$field[$i]}\"";
2524
            }
2525
        }
2526
        if (count($primarykeycolumns) > 0) {
2527
            $sql .= ', PRIMARY KEY ('.implode(', ', $primarykeycolumns).')';
2528
        }
2529
2530
        $sql .= ')';
2531
2532
        // WITHOUT OIDS
2533
        if ($withoutoids) {
2534
            $sql .= ' WITHOUT OIDS';
2535
        } else {
2536
            $sql .= ' WITH OIDS';
2537
        }
2538
2539
        // Tablespace
2540
        if ($this->hasTablespaces() && $tablespace != '') {
2541
            $this->fieldClean($tablespace);
2542
            $sql .= " TABLESPACE \"{$tablespace}\"";
2543
        }
2544
2545
        $status = $this->execute($sql);
2546
        if ($status) {
2547
            $this->rollbackTransaction();
2548
2549
            return -1;
2550
        }
2551
2552 View Code Duplication
        if ($tblcomment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2553
            $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
2554
            if ($status) {
2555
                $this->rollbackTransaction();
2556
2557
                return -1;
2558
            }
2559
        }
2560
2561 View Code Duplication
        if ($comment_sql != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2562
            $status = $this->execute($comment_sql);
2563
            if ($status) {
2564
                $this->rollbackTransaction();
2565
2566
                return -1;
2567
            }
2568
        }
2569
2570
        return $this->endTransaction();
2571
    }
2572
2573
    /**
2574
     * Creates a new table in the database copying attribs and other properties from another table.
2575
     *
2576
     * @param                                 $name        The name of the table
2577
     * @param                                 $like        an array giving the schema ans the name of the table from which attribs are copying
2578
     *                                                     from: array(
2579
     *                                                     'table' => table name,
2580
     *                                                     'schema' => the schema name,
2581
     *                                                     )
2582
     * @param bool|\PHPPgAdmin\Database\if    $defaults    if true, copy the defaults values as well
2583
     * @param bool|\PHPPgAdmin\Database\if    $constraints if true, copy the constraints as well (CHECK on table & attr)
2584
     * @param bool                            $idx
2585
     * @param \PHPPgAdmin\Database\The|string $tablespace  The tablespace name ('' means none/default)
2586
     *
2587
     * @return bool|int
2588
     */
2589
    public function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '')
2590
    {
2591
        $f_schema = $this->_schema;
2592
        $this->fieldClean($f_schema);
2593
        $this->fieldClean($name);
2594
        $this->fieldClean($like['schema']);
2595
        $this->fieldClean($like['table']);
2596
        $like = "\"{$like['schema']}\".\"{$like['table']}\"";
2597
2598
        $status = $this->beginTransaction();
2599
        if ($status != 0) {
2600
            return -1;
2601
        }
2602
2603
        $sql = "CREATE TABLE \"{$f_schema}\".\"{$name}\" (LIKE {$like}";
2604
2605
        if ($defaults) {
2606
            $sql .= ' INCLUDING DEFAULTS';
2607
        }
2608
2609
        if ($this->hasCreateTableLikeWithConstraints() && $constraints) {
2610
            $sql .= ' INCLUDING CONSTRAINTS';
2611
        }
2612
2613
        if ($this->hasCreateTableLikeWithIndexes() && $idx) {
2614
            $sql .= ' INCLUDING INDEXES';
2615
        }
2616
2617
        $sql .= ')';
2618
2619
        if ($this->hasTablespaces() && $tablespace != '') {
2620
            $this->fieldClean($tablespace);
2621
            $sql .= " TABLESPACE \"{$tablespace}\"";
2622
        }
2623
2624
        $status = $this->execute($sql);
2625
        if ($status) {
2626
            $this->rollbackTransaction();
2627
2628
            return -1;
2629
        }
2630
2631
        return $this->endTransaction();
2632
    }
2633
2634
    public function hasCreateTableLikeWithConstraints()
2635
    {
2636
        return true;
2637
    }
2638
2639
    public function hasCreateTableLikeWithIndexes()
2640
    {
2641
        return true;
2642
    }
2643
2644
    /**
2645
     * Alter table properties.
2646
     *
2647
     * @param $table      The name of the table
2648
     * @param $name       The new name for the table
2649
     * @param $owner      The new owner for the table
2650
     * @param $schema     The new schema for the table
2651
     * @param $comment    The comment on the table
2652
     * @param $tablespace The new tablespace for the table ('' means leave as is)
2653
     *
2654
     * @return bool|int 0 success
2655
     */
2656 View Code Duplication
    public function alterTable($table, $name, $owner, $schema, $comment, $tablespace)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2657
    {
2658
        $data = $this->getTable($table);
2659
2660
        if ($data->recordCount() != 1) {
2661
            return -2;
2662
        }
2663
2664
        $status = $this->beginTransaction();
2665
        if ($status != 0) {
2666
            $this->rollbackTransaction();
2667
2668
            return -1;
2669
        }
2670
2671
        $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
2672
2673
        if ($status != 0) {
2674
            $this->rollbackTransaction();
2675
2676
            return $status;
2677
        }
2678
2679
        return $this->endTransaction();
2680
    }
2681
2682
    /**
2683
     * Protected method which alter a table
2684
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
2685
     *
2686
     * @param $tblrs      The table recordSet returned by getTable()
2687
     * @param $name       The new name for the table
2688
     * @param $owner      The new owner for the table
2689
     * @param $schema     The new schema for the table
2690
     * @param $comment    The comment on the table
2691
     * @param $tablespace The new tablespace for the table ('' means leave as is)
2692
     *
2693
     * @return int 0 success
2694
     */
2695
    protected function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace)
2696
    {
2697
        $this->fieldArrayClean($tblrs->fields);
2698
2699
        // Comment
2700
        $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
2701
        if ($status != 0) {
2702
            return -4;
2703
        }
2704
2705
        // Owner
2706
        $this->fieldClean($owner);
2707
        $status = $this->alterTableOwner($tblrs, $owner);
2708
        if ($status != 0) {
2709
            return -5;
2710
        }
2711
2712
        // Tablespace
2713
        $this->fieldClean($tablespace);
2714
        $status = $this->alterTableTablespace($tblrs, $tablespace);
2715
        if ($status != 0) {
2716
            return -6;
2717
        }
2718
2719
        // Rename
2720
        $this->fieldClean($name);
2721
        $status = $this->alterTableName($tblrs, $name);
2722
        if ($status != 0) {
2723
            return -3;
2724
        }
2725
2726
        // Schema
2727
        $this->fieldClean($schema);
2728
        $status = $this->alterTableSchema($tblrs, $schema);
2729
        if ($status != 0) {
2730
            return -7;
2731
        }
2732
2733
        return 0;
2734
    }
2735
2736
    /**
2737
     * Alter a table's owner
2738
     * /!\ this function is called from _alterTable which take care of escaping fields.
2739
     *
2740
     * @param      $tblrs The table RecordSet returned by getTable()
2741
     * @param null $owner
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $owner is correct as it would always require null to be passed?
Loading history...
2742
     *
2743
     * @return int|\PHPPgAdmin\Database\A 0 success
2744
     *
2745
     * @internal param \PHPPgAdmin\Database\The $name new table's owner
2746
     */
2747 View Code Duplication
    public function alterTableOwner($tblrs, $owner = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2748
    {
2749
        /* vars cleaned in _alterTable */
2750
        if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
2751
            $f_schema = $this->_schema;
2752
            $this->fieldClean($f_schema);
2753
            // If owner has been changed, then do the alteration.  We are
2754
            // careful to avoid this generally as changing owner is a
2755
            // superuser only function.
2756
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";
2757
2758
            return $this->execute($sql);
2759
        }
2760
2761
        return 0;
2762
    }
2763
2764
    /**
2765
     * Alter a table's tablespace
2766
     * /!\ this function is called from _alterTable which take care of escaping fields.
2767
     *
2768
     * @param      $tblrs      The table RecordSet returned by getTable()
2769
     * @param null $tablespace
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $tablespace is correct as it would always require null to be passed?
Loading history...
2770
     *
2771
     * @return int|\PHPPgAdmin\Database\A 0 success
2772
     *
2773
     * @internal param \PHPPgAdmin\Database\The $name new table's tablespace
2774
     */
2775 View Code Duplication
    public function alterTableTablespace($tblrs, $tablespace = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2776
    {
2777
        /* vars cleaned in _alterTable */
2778
        if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
2779
            $f_schema = $this->_schema;
2780
            $this->fieldClean($f_schema);
2781
2782
            // If tablespace has been changed, then do the alteration.  We
2783
            // don't want to do this unnecessarily.
2784
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";
2785
2786
            return $this->execute($sql);
2787
        }
2788
2789
        return 0;
2790
    }
2791
2792
    /**
2793
     * Alter a table's name
2794
     * /!\ this function is called from _alterTable which take care of escaping fields.
2795
     *
2796
     * @param $tblrs The table RecordSet returned by getTable()
2797
     * @param $name  The new table's name
2798
     *
2799
     * @return int|\PHPPgAdmin\Database\A 0 success
2800
     */
2801 View Code Duplication
    public function alterTableName($tblrs, $name = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2802
    {
2803
        /* vars cleaned in _alterTable */
2804
        // Rename (only if name has changed)
2805
        if (!empty($name) && ($name != $tblrs->fields['relname'])) {
2806
            $f_schema = $this->_schema;
2807
            $this->fieldClean($f_schema);
2808
2809
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\"";
2810
            $status = $this->execute($sql);
2811
            if ($status == 0) {
2812
                $tblrs->fields['relname'] = $name;
2813
            } else {
2814
                return $status;
2815
            }
2816
        }
2817
2818
        return 0;
2819
    }
2820
2821
    // Row functions
2822
2823
    /**
2824
     * Alter a table's schema
2825
     * /!\ this function is called from _alterTable which take care of escaping fields.
2826
     *
2827
     * @param      $tblrs  The table RecordSet returned by getTable()
2828
     * @param null $schema
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $schema is correct as it would always require null to be passed?
Loading history...
2829
     *
2830
     * @return int|\PHPPgAdmin\Database\A 0 success
2831
     *
2832
     * @internal param \PHPPgAdmin\Database\The $name new table's schema
2833
     */
2834 View Code Duplication
    public function alterTableSchema($tblrs, $schema = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2835
    {
2836
        /* vars cleaned in _alterTable */
2837
        if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
2838
            $f_schema = $this->_schema;
2839
            $this->fieldClean($f_schema);
2840
            // If tablespace has been changed, then do the alteration.  We
2841
            // don't want to do this unnecessarily.
2842
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
2843
2844
            return $this->execute($sql);
2845
        }
2846
2847
        return 0;
2848
    }
2849
2850
    /**
2851
     * Empties a table in the database.
2852
     *
2853
     * @param $table The table to be emptied
2854
     *
2855
     * @return \PHPPgAdmin\Database\A 0 success
2856
     */
2857
    public function emptyTable($table)
2858
    {
2859
        $f_schema = $this->_schema;
2860
        $this->fieldClean($f_schema);
2861
        $this->fieldClean($table);
2862
2863
        $sql = "DELETE FROM \"{$f_schema}\".\"{$table}\"";
2864
2865
        return $this->execute($sql);
2866
    }
2867
2868
    /**
2869
     * Removes a table from the database.
2870
     *
2871
     * @param $table   The table to drop
2872
     * @param $cascade True to cascade drop, false to restrict
2873
     *
2874
     * @return \PHPPgAdmin\Database\A 0 success
2875
     */
2876 View Code Duplication
    public function dropTable($table, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2877
    {
2878
        $f_schema = $this->_schema;
2879
        $this->fieldClean($f_schema);
2880
        $this->fieldClean($table);
2881
2882
        $sql = "DROP TABLE \"{$f_schema}\".\"{$table}\"";
2883
        if ($cascade) {
2884
            $sql .= ' CASCADE';
2885
        }
2886
2887
        return $this->execute($sql);
2888
    }
2889
2890
    /**
2891
     * Add a new column to a table.
2892
     *
2893
     * @param $table   The table to add to
2894
     * @param $column  The name of the new column
2895
     * @param $type    The type of the column
2896
     * @param $array   True if array type, false otherwise
2897
     * @param $length  The optional size of the column (ie. 30 for varchar(30))
2898
     * @param $notnull True if NOT NULL, false otherwise
2899
     * @param $default The default for the column.  '' for none.
2900
     * @param $comment
2901
     *
2902
     * @return bool|int 0 success
2903
     */
2904
    public function addColumn($table, $column, $type, $array, $length, $notnull, $default, $comment)
2905
    {
2906
        $f_schema = $this->_schema;
2907
        $this->fieldClean($f_schema);
2908
        $this->fieldClean($table);
2909
        $this->fieldClean($column);
2910
        $this->clean($type);
2911
        $this->clean($length);
2912
2913
        if ($length == '') {
2914
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}";
2915
        } else {
2916
            switch ($type) {
2917
                // Have to account for weird placing of length for with/without
2918
                // time zone types
2919
                case 'timestamp with time zone':
2920 View Code Duplication
                case 'timestamp without time zone':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2921
                    $qual = substr($type, 9);
2922
                    $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" timestamp({$length}){$qual}";
2923
                    break;
2924
                case 'time with time zone':
2925 View Code Duplication
                case 'time without time zone':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
2926
                    $qual = substr($type, 4);
2927
                    $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}";
2928
                    break;
2929
                default:
2930
                    $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}({$length})";
2931
            }
2932
        }
2933
2934
        // Add array qualifier, if requested
2935
        if ($array) {
2936
            $sql .= '[]';
2937
        }
2938
2939
        // If we have advanced column adding, add the extra qualifiers
2940
        if ($this->hasCreateFieldWithConstraints()) {
2941
            // NOT NULL clause
2942
            if ($notnull) {
2943
                $sql .= ' NOT NULL';
2944
            }
2945
2946
            // DEFAULT clause
2947
            if ($default != '') {
2948
                $sql .= ' DEFAULT '.$default;
2949
            }
2950
        }
2951
2952
        $status = $this->beginTransaction();
2953
        if ($status != 0) {
2954
            return -1;
2955
        }
2956
2957
        $status = $this->execute($sql);
2958
        if ($status != 0) {
2959
            $this->rollbackTransaction();
2960
2961
            return -1;
2962
        }
2963
2964
        $status = $this->setComment('COLUMN', $column, $table, $comment);
2965
        if ($status != 0) {
2966
            $this->rollbackTransaction();
2967
2968
            return -1;
2969
        }
2970
2971
        return $this->endTransaction();
2972
    }
2973
2974
    // Sequence functions
2975
2976
    public function hasCreateFieldWithConstraints()
2977
    {
2978
        return true;
2979
    }
2980
2981
    /**
2982
     * Alters a column in a table.
2983
     *
2984
     * @param $table      The table in which the column resides
2985
     * @param $column     The column to alter
2986
     * @param $name       The new name for the column
2987
     * @param $notnull    (boolean) True if not null, false otherwise
2988
     * @param $oldnotnull (boolean) True if column is already not null, false otherwise
2989
     * @param $default    The new default for the column
2990
     * @param $olddefault The old default for the column
2991
     * @param $type       The new type for the column
2992
     * @param $length     The optional size of the column (ie. 30 for varchar(30))
2993
     * @param $array      True if array type, false otherwise
2994
     * @param $oldtype    The old type for the column
2995
     * @param $comment    Comment for the column
2996
     *
2997
     * @return array 0 success
2998
     */
2999
    public function alterColumn(
3000
        $table,
3001
        $column,
3002
        $name,
3003
        $notnull,
3004
        $oldnotnull,
3005
        $default,
3006
        $olddefault,
3007
        $type,
3008
        $length,
3009
        $array,
3010
        $oldtype,
3011
        $comment
3012
    ) {
3013
        // Begin transaction
3014
        $status = $this->beginTransaction();
3015
        $sql = '';
3016
        if ($status != 0) {
3017
            $this->rollbackTransaction();
3018
3019
            return [-6, $sql];
3020
        }
3021
3022
        // Rename the column, if it has been changed
3023 View Code Duplication
        if ($column != $name) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3024
            $status = $this->renameColumn($table, $column, $name);
3025
            if ($status != 0) {
3026
                $this->rollbackTransaction();
3027
3028
                return [-4, $sql];
3029
            }
3030
        }
3031
3032
        $f_schema = $this->_schema;
3033
        $this->fieldClean($f_schema);
3034
        $this->fieldClean($name);
3035
        $this->fieldClean($table);
3036
        $this->fieldClean($column);
3037
3038
        $toAlter = [];
3039
        // Create the command for changing nullability
3040
        if ($notnull != $oldnotnull) {
3041
            $toAlter[] = "ALTER COLUMN \"{$name}\" ".($notnull ? 'SET' : 'DROP').' NOT NULL';
3042
        }
3043
3044
        // Add default, if it has changed
3045
        if ($default != $olddefault) {
3046
            if ($default == '') {
3047
                $toAlter[] = "ALTER COLUMN \"{$name}\" DROP DEFAULT";
3048
            } else {
3049
                $toAlter[] = "ALTER COLUMN \"{$name}\" SET DEFAULT {$default}";
3050
            }
3051
        }
3052
3053
        // Add type, if it has changed
3054
        if ($length == '') {
3055
            $ftype = $type;
3056
        } else {
3057
            switch ($type) {
3058
                // Have to account for weird placing of length for with/without
3059
                // time zone types
3060
                case 'timestamp with time zone':
3061
                case 'timestamp without time zone':
3062
                    $qual = substr($type, 9);
3063
                    $ftype = "timestamp({$length}){$qual}";
3064
                    break;
3065
                case 'time with time zone':
3066
                case 'time without time zone':
3067
                    $qual = substr($type, 4);
3068
                    $ftype = "time({$length}){$qual}";
3069
                    break;
3070
                default:
3071
                    $ftype = "{$type}({$length})";
3072
            }
3073
        }
3074
3075
        // Add array qualifier, if requested
3076
        if ($array) {
3077
            $ftype .= '[]';
3078
        }
3079
3080
        if ($ftype != $oldtype) {
3081
            $toAlter[] = "ALTER COLUMN \"{$name}\" TYPE {$ftype}";
3082
        }
3083
3084
        // Attempt to process the batch alteration, if anything has been changed
3085
        if (!empty($toAlter)) {
3086
            // Initialise an empty SQL string
3087
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" "
3088
            .implode(',', $toAlter);
3089
3090
            $status = $this->execute($sql);
3091
            if ($status != 0) {
3092
                $this->rollbackTransaction();
3093
3094
                return [-1, $sql];
3095
            }
3096
        }
3097
3098
        // Update the comment on the column
3099
        $status = $this->setComment('COLUMN', $name, $table, $comment);
3100
        if ($status != 0) {
3101
            $this->rollbackTransaction();
3102
3103
            return [-5, $sql];
3104
        }
3105
3106
        return [$this->endTransaction(), $sql];
3107
    }
3108
3109
    /**
3110
     * Renames a column in a table.
3111
     *
3112
     * @param $table   The table containing the column to be renamed
3113
     * @param $column  The column to be renamed
3114
     * @param $newName The new name for the column
3115
     *
3116
     * @return \PHPPgAdmin\Database\A 0 success
3117
     */
3118
    public function renameColumn($table, $column, $newName)
3119
    {
3120
        $f_schema = $this->_schema;
3121
        $this->fieldClean($f_schema);
3122
        $this->fieldClean($table);
3123
        $this->fieldClean($column);
3124
        $this->fieldClean($newName);
3125
3126
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
3127
3128
        return $this->execute($sql);
3129
    }
3130
3131
    /**
3132
     * Sets default value of a column.
3133
     *
3134
     * @param $table   The table from which to drop
3135
     * @param $column  The column name to set
3136
     * @param $default The new default value
3137
     *
3138
     * @return \PHPPgAdmin\Database\A 0 success
3139
     */
3140 View Code Duplication
    public function setColumnDefault($table, $column, $default)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3141
    {
3142
        $f_schema = $this->_schema;
3143
        $this->fieldClean($f_schema);
3144
        $this->fieldClean($table);
3145
        $this->fieldClean($column);
3146
3147
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
3148
3149
        return $this->execute($sql);
3150
    }
3151
3152
    /**
3153
     * Sets whether or not a column can contain NULLs.
3154
     *
3155
     * @param $table  The table that contains the column
3156
     * @param $column The column to alter
3157
     * @param $state  True to set null, false to set not null
3158
     *
3159
     * @return \PHPPgAdmin\Database\A 0 success
3160
     */
3161
    public function setColumnNull($table, $column, $state)
3162
    {
3163
        $f_schema = $this->_schema;
3164
        $this->fieldClean($f_schema);
3165
        $this->fieldClean($table);
3166
        $this->fieldClean($column);
3167
3168
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" ".($state ? 'DROP' : 'SET').' NOT NULL';
3169
3170
        return $this->execute($sql);
3171
    }
3172
3173
    /**
3174
     * Drops a column from a table.
3175
     *
3176
     * @param $table   The table from which to drop a column
3177
     * @param $column  The column to be dropped
3178
     * @param $cascade True to cascade drop, false to restrict
3179
     *
3180
     * @return \PHPPgAdmin\Database\A 0 success
3181
     */
3182 View Code Duplication
    public function dropColumn($table, $column, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3183
    {
3184
        $f_schema = $this->_schema;
3185
        $this->fieldClean($f_schema);
3186
        $this->fieldClean($table);
3187
        $this->fieldClean($column);
3188
3189
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DROP COLUMN \"{$column}\"";
3190
        if ($cascade) {
3191
            $sql .= ' CASCADE';
3192
        }
3193
3194
        return $this->execute($sql);
3195
    }
3196
3197
    /**
3198
     * Drops default value of a column.
3199
     *
3200
     * @param $table  The table from which to drop
3201
     * @param $column The column name to drop default
3202
     *
3203
     * @return \PHPPgAdmin\Database\A 0 success
3204
     */
3205 View Code Duplication
    public function dropColumnDefault($table, $column)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3206
    {
3207
        $f_schema = $this->_schema;
3208
        $this->fieldClean($f_schema);
3209
        $this->fieldClean($table);
3210
        $this->fieldClean($column);
3211
3212
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
3213
3214
        return $this->execute($sql);
3215
    }
3216
3217
    /**
3218
     * Sets up the data object for a dump.  eg. Starts the appropriate
3219
     * transaction, sets variables, etc.
3220
     *
3221
     * @return int 0 success
3222
     */
3223
    public function beginDump()
3224
    {
3225
        // Begin serializable transaction (to dump consistent data)
3226
        $status = $this->beginTransaction();
3227
        if ($status != 0) {
3228
            return -1;
3229
        }
3230
3231
        // Set serializable
3232
        $sql = 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE';
3233
        $status = $this->execute($sql);
3234
        if ($status != 0) {
3235
            $this->rollbackTransaction();
3236
3237
            return -1;
3238
        }
3239
3240
        // Set datestyle to ISO
3241
        $sql = 'SET DATESTYLE = ISO';
3242
        $status = $this->execute($sql);
3243
        if ($status != 0) {
3244
            $this->rollbackTransaction();
3245
3246
            return -1;
3247
        }
3248
3249
        // Set extra_float_digits to 2
3250
        $sql = 'SET extra_float_digits TO 2';
3251
        $status = $this->execute($sql);
3252
        if ($status != 0) {
3253
            $this->rollbackTransaction();
3254
3255
            return -1;
3256
        }
3257
3258
        return 0;
3259
    }
3260
3261
    /**
3262
     * Ends the data object for a dump.
3263
     *
3264
     * @return bool 0 success
3265
     */
3266
    public function endDump()
3267
    {
3268
        return $this->endTransaction();
3269
    }
3270
3271
    /**
3272
     * Returns a recordset of all columns in a relation.  Used for data export.
3273
     *
3274
     * @@ Note: Really needs to use a cursor
3275
     *
3276
     * @param $relation The name of a relation
3277
     * @param $oids
3278
     *
3279
     * @return \PHPPgAdmin\Database\A recordset on success
3280
     */
3281
    public function dumpRelation($relation, $oids)
3282
    {
3283
        $this->fieldClean($relation);
3284
3285
        // Actually retrieve the rows
3286
        if ($oids) {
3287
            $oid_str = $this->id.', ';
3288
        } else {
3289
            $oid_str = '';
3290
        }
3291
3292
        return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
3293
    }
3294
3295
    /**
3296
     * Returns all available autovacuum per table information.
3297
     *
3298
     * @param \PHPPgAdmin\Database\if|string $table if given, return autovacuum info for the given table or return all informations for all table
3299
     *
3300
     * @return \PHPPgAdmin\Database\A recordset
3301
     */
3302
    public function getTableAutovacuum($table = '')
3303
    {
3304
        $sql = '';
3305
3306
        if ($table !== '') {
3307
            $this->clean($table);
3308
            $c_schema = $this->_schema;
3309
            $this->clean($c_schema);
3310
3311
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
3312
				FROM pg_class c
3313
					LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
3314
				WHERE c.relkind = 'r'::\"char\"
3315
					AND n.nspname NOT IN ('pg_catalog','information_schema')
3316
					AND c.reloptions IS NOT NULL
3317
					AND c.relname = '{$table}' AND n.nspname = '{$c_schema}'
3318
				ORDER BY nspname, relname";
3319
        } else {
3320
            $sql = "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
3321
				FROM pg_class c
3322
					LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
3323
				WHERE c.relkind = 'r'::\"char\"
3324
					AND n.nspname NOT IN ('pg_catalog','information_schema')
3325
					AND c.reloptions IS NOT NULL
3326
				ORDER BY nspname, relname";
3327
        }
3328
3329
        /* tmp var to parse the results */
3330
        $_autovacs = $this->selectSet($sql);
3331
3332
        /* result aray to return as RS */
3333
        $autovacs = [];
3334
        while (!$_autovacs->EOF) {
3335
            $_ = [
3336
                'nspname' => $_autovacs->fields['nspname'],
3337
                'relname' => $_autovacs->fields['relname'],
3338
            ];
3339
3340
            foreach (explode(',', $_autovacs->fields['reloptions']) as $var) {
3341
                list($o, $v) = explode('=', $var);
3342
                $_[$o] = $v;
3343
            }
3344
3345
            $autovacs[] = $_;
3346
3347
            $_autovacs->moveNext();
3348
        }
3349
3350
        return new \PHPPgAdmin\ArrayRecordSet($autovacs);
3351
    }
3352
3353
    /**
3354
     * Get the fields for uniquely identifying a row in a table.
3355
     *
3356
     * @param $table The table for which to retrieve the identifier
3357
     *
3358
     * @return An array mapping attribute number to attribute name, empty for no identifiers
3359
     * @return -1 error
3360
     */
3361
    public function getRowIdentifier($table)
3362
    {
3363
        $oldtable = $table;
3364
        $c_schema = $this->_schema;
3365
        $this->clean($c_schema);
3366
        $this->clean($table);
3367
3368
        $status = $this->beginTransaction();
3369
        if ($status != 0) {
3370
            return -1;
3371
        }
3372
3373
        // Get the first primary or unique index (sorting primary keys first) that
3374
        // is NOT a partial index.
3375
        $sql = "
3376
			SELECT indrelid, indkey
3377
			FROM pg_catalog.pg_index
3378
			WHERE indisunique AND indrelid=(
3379
				SELECT oid FROM pg_catalog.pg_class
3380
				WHERE relname='{$table}' AND relnamespace=(
3381
					SELECT oid FROM pg_catalog.pg_namespace
3382
					WHERE nspname='{$c_schema}'
3383
				)
3384
			) AND indpred IS NULL AND indexprs IS NULL
3385
			ORDER BY indisprimary DESC LIMIT 1";
3386
        $rs = $this->selectSet($sql);
3387
3388
        // If none, check for an OID column.  Even though OIDs can be duplicated, the edit and delete row
3389
        // functions check that they're only modiying a single row.  Otherwise, return empty array.
3390
        if ($rs->recordCount() == 0) {
3391
            // Check for OID column
3392
            $temp = [];
3393
            if ($this->hasObjectID($table)) {
3394
                $temp = ['oid'];
3395
            }
3396
            $this->endTransaction();
3397
3398
            return $temp;
3399
        } // Otherwise find the names of the keys
3400
3401
        $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
3402
        if (!is_array($attnames)) {
3403
            $this->rollbackTransaction();
3404
3405
            return -1;
3406
        }
3407
3408
        $this->endTransaction();
3409
3410
        return $attnames;
3411
    }
3412
3413
    /**
3414
     * Adds a new row to a table.
3415
     *
3416
     * @param $table  The table in which to insert
3417
     * @param $fields Array of given field in values
3418
     * @param $values Array of new values for the row
3419
     * @param $nulls  An array mapping column => something if it is to be null
3420
     * @param $format An array of the data type (VALUE or EXPRESSION)
3421
     * @param $types  An array of field types
3422
     *
3423
     * @return int|\PHPPgAdmin\Database\A 0 success
3424
     */
3425
    public function insertRow($table, $fields, $values, $nulls, $format, $types)
3426
    {
3427
        if (!is_array($fields) || !is_array($values) || !is_array($nulls)
3428
            || !is_array($format) || !is_array($types)
3429
            || (count($fields) != count($values))
3430
        ) {
3431
            return -1;
3432
        }
3433
3434
        // Build clause
3435
        if (count($values) > 0) {
3436
            // Escape all field names
3437
            $fields = array_map(['\PHPPgAdmin\Database\Postgres', 'fieldClean'], $fields);
3438
            $f_schema = $this->_schema;
3439
            $this->fieldClean($table);
3440
            $this->fieldClean($f_schema);
3441
3442
            $sql = '';
3443
            foreach ($values as $i => $value) {
3444
3445
                // Handle NULL values
3446
                if (isset($nulls[$i])) {
3447
                    $sql .= ',NULL';
3448
                } else {
3449
                    $sql .= ','.$this->formatValue($types[$i], $format[$i], $value);
3450
                }
3451
            }
3452
3453
            $sql = "INSERT INTO \"{$f_schema}\".\"{$table}\" (\"".implode('","', $fields).'")
3454
                VALUES ('.substr($sql, 1).')';
3455
3456
            return $this->execute($sql);
3457
        }
3458
3459
        return -1;
3460
    }
3461
3462
    /**
3463
     * Formats a value or expression for sql purposes.
3464
     *
3465
     * @param $type   The type of the field
3466
     * @param $format VALUE or EXPRESSION
3467
     * @param $value  The actual value entered in the field.  Can be NULL
3468
     *
3469
     * @return The suitably quoted and escaped value.
3470
     */
3471
    public function formatValue($type, $format, $value)
3472
    {
3473
        switch ($type) {
3474
            case 'bool':
3475
            case 'boolean':
3476
                if ($value == 't') {
3477
                    return 'TRUE';
3478
                }
3479
3480
                if ($value == 'f') {
3481
                    return 'FALSE';
3482
                } elseif ($value == '') {
3483
                    return 'NULL';
3484
                } else {
3485
                    return $value;
3486
                }
3487
3488
                break;
3489
            default:
3490
                // Checking variable fields is difficult as there might be a size
3491
                // attribute...
3492
                if (strpos($type, 'time') === 0) {
3493
                    // Assume it's one of the time types...
3494
                    if ($value == '') {
3495
                        return "''";
3496
                    }
3497
3498
                    if (strcasecmp($value, 'CURRENT_TIMESTAMP') == 0
3499
                        || strcasecmp($value, 'CURRENT_TIME') == 0
3500
                        || strcasecmp($value, 'CURRENT_DATE') == 0
3501
                        || strcasecmp($value, 'LOCALTIME') == 0
3502
                        || strcasecmp($value, 'LOCALTIMESTAMP') == 0) {
3503
                        return $value;
3504
                    } elseif ($format == 'EXPRESSION') {
3505
                        return $value;
3506
                    } else {
3507
                        $this->clean($value);
3508
3509
                        return "'{$value}'";
3510
                    }
3511
                } else {
3512
                    if ($format == 'VALUE') {
3513
                        $this->clean($value);
3514
3515
                        return "'{$value}'";
3516
                    }
3517
3518
                    return $value;
3519
                }
3520
        }
3521
    }
3522
3523
    // View functions
3524
3525
    /**
3526
     * Updates a row in a table.
3527
     *
3528
     * @param $table  The table in which to update
3529
     * @param $vars   An array mapping new values for the row
3530
     * @param $nulls  An array mapping column => something if it is to be null
3531
     * @param $format An array of the data type (VALUE or EXPRESSION)
3532
     * @param $types  An array of field types
3533
     * @param $keyarr An array mapping column => value to update
3534
     *
3535
     * @return bool|int 0 success
3536
     */
3537
    public function editRow($table, $vars, $nulls, $format, $types, $keyarr)
3538
    {
3539
        if (!is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types)) {
3540
            return -1;
3541
        }
3542
3543
        $f_schema = $this->_schema;
3544
        $this->fieldClean($f_schema);
3545
        $this->fieldClean($table);
3546
3547
        // Build clause
3548
        if (count($vars) > 0) {
3549
            foreach ($vars as $key => $value) {
3550
                $this->fieldClean($key);
3551
3552
                // Handle NULL values
3553
                if (isset($nulls[$key])) {
3554
                    $tmp = 'NULL';
3555
                } else {
3556
                    $tmp = $this->formatValue($types[$key], $format[$key], $value);
3557
                }
3558
3559
                if (isset($sql)) {
3560
                    $sql .= ", \"{$key}\"={$tmp}";
3561
                } else {
3562
                    $sql = "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
3563
                }
3564
            }
3565
            $first = true;
3566
            foreach ($keyarr as $k => $v) {
3567
                $this->fieldClean($k);
3568
                $this->clean($v);
3569
                if ($first) {
3570
                    $sql .= " WHERE \"{$k}\"='{$v}'";
3571
                    $first = false;
3572
                } else {
3573
                    $sql .= " AND \"{$k}\"='{$v}'";
3574
                }
3575
            }
3576
        }
3577
3578
        // Begin transaction.  We do this so that we can ensure only one row is
3579
        // edited
3580
        $status = $this->beginTransaction();
3581
        if ($status != 0) {
3582
            $this->rollbackTransaction();
3583
3584
            return -1;
3585
        }
3586
3587
        $status = $this->execute($sql);
3588
        if ($status != 0) {
3589
            // update failed
3590
            $this->rollbackTransaction();
3591
3592
            return -1;
3593
        }
3594
3595
        if ($this->conn->Affected_Rows() != 1) {
3596
            // more than one row could be updated
3597
            $this->rollbackTransaction();
3598
3599
            return -2;
3600
        }
3601
3602
        // End transaction
3603
        return $this->endTransaction();
3604
    }
3605
3606
    /**
3607
     * Delete a row from a table.
3608
     *
3609
     * @param      $table  The table from which to delete
3610
     * @param      $key    An array mapping column => value to delete
3611
     * @param bool $schema
3612
     *
3613
     * @return bool|int 0 success
3614
     */
3615
    public function deleteRow($table, $key, $schema = false)
3616
    {
3617
        if (!is_array($key)) {
3618
            return -1;
3619
        }
3620
3621
        // Begin transaction.  We do this so that we can ensure only one row is
3622
        // deleted
3623
        $status = $this->beginTransaction();
3624
        if ($status != 0) {
3625
            $this->rollbackTransaction();
3626
3627
            return -1;
3628
        }
3629
3630
        if ($schema === false) {
3631
            $schema = $this->_schema;
3632
        }
3633
3634
        $status = $this->delete($table, $key, $schema);
3635
        if ($status != 0 || $this->conn->Affected_Rows() != 1) {
3636
            $this->rollbackTransaction();
3637
3638
            return -2;
3639
        }
3640
3641
        // End transaction
3642
        return $this->endTransaction();
3643
    }
3644
3645
    /**
3646
     * Returns all sequences in the current database.
3647
     *
3648
     * @param bool $all
3649
     *
3650
     * @return \PHPPgAdmin\Database\A recordset
3651
     */
3652
    public function getSequences($all = false)
3653
    {
3654
        if ($all) {
3655
            // Exclude pg_catalog and information_schema tables
3656
            $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
3657
				FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
3658
				WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
3659
				AND c.relkind = 'S'
3660
				AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
3661
				ORDER BY nspname, seqname";
3662
        } else {
3663
            $c_schema = $this->_schema;
3664
            $this->clean($c_schema);
3665
            $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment,
3666
				(SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
3667
				FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
3668
				WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
3669
				AND c.relkind = 'S' AND n.nspname='{$c_schema}' ORDER BY seqname";
3670
        }
3671
3672
        return $this->selectSet($sql);
3673
    }
3674
3675
    /**
3676
     * Execute nextval on a given sequence.
3677
     *
3678
     * @param $sequence Sequence name
3679
     *
3680
     * @return \PHPPgAdmin\Database\A 0 success
3681
     */
3682
    public function nextvalSequence($sequence)
3683
    {
3684
        /* This double-cleaning is deliberate */
3685
        $f_schema = $this->_schema;
3686
        $this->fieldClean($f_schema);
3687
        $this->clean($f_schema);
3688
        $this->fieldClean($sequence);
3689
        $this->clean($sequence);
3690
3691
        $sql = "SELECT pg_catalog.NEXTVAL('\"{$f_schema}\".\"{$sequence}\"')";
3692
3693
        return $this->execute($sql);
3694
    }
3695
3696
    /**
3697
     * Execute setval on a given sequence.
3698
     *
3699
     * @param $sequence  Sequence name
3700
     * @param $nextvalue The next value
3701
     *
3702
     * @return \PHPPgAdmin\Database\A 0 success
3703
     */
3704
    public function setvalSequence($sequence, $nextvalue)
3705
    {
3706
        /* This double-cleaning is deliberate */
3707
        $f_schema = $this->_schema;
3708
        $this->fieldClean($f_schema);
3709
        $this->clean($f_schema);
3710
        $this->fieldClean($sequence);
3711
        $this->clean($sequence);
3712
        $this->clean($nextvalue);
3713
3714
        $sql = "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', '{$nextvalue}')";
3715
3716
        return $this->execute($sql);
3717
    }
3718
3719
    /**
3720
     * Restart a given sequence to its start value.
3721
     *
3722
     * @param $sequence Sequence name
3723
     *
3724
     * @return \PHPPgAdmin\Database\A 0 success
3725
     */
3726 View Code Duplication
    public function restartSequence($sequence)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
3727
    {
3728
        $f_schema = $this->_schema;
3729
        $this->fieldClean($f_schema);
3730
        $this->fieldClean($sequence);
3731
3732
        $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$sequence}\" RESTART;";
3733
3734
        return $this->execute($sql);
3735
    }
3736
3737
    /**
3738
     * Resets a given sequence to min value of sequence.
3739
     *
3740
     * @param $sequence Sequence name
3741
     *
3742
     * @return int|\PHPPgAdmin\Database\A 0 success
3743
     */
3744
    public function resetSequence($sequence)
3745
    {
3746
        // Get the minimum value of the sequence
3747
        $seq = $this->getSequence($sequence);
3748
        if ($seq->recordCount() != 1) {
3749
            return -1;
3750
        }
3751
3752
        $minvalue = $seq->fields['min_value'];
3753
3754
        $f_schema = $this->_schema;
3755
        $this->fieldClean($f_schema);
3756
        /* This double-cleaning is deliberate */
3757
        $this->fieldClean($sequence);
3758
        $this->clean($sequence);
3759
3760
        $sql = "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', {$minvalue})";
3761
3762
        return $this->execute($sql);
3763
    }
3764
3765
    /**
3766
     * Returns properties of a single sequence.
3767
     *
3768
     * @param $sequence Sequence name
3769
     *
3770
     * @return A recordset
3771
     */
3772
    public function getSequence($sequence)
3773
    {
3774
        $c_schema = $this->_schema;
3775
        $this->clean($c_schema);
3776
        $c_sequence = $sequence;
3777
        $this->fieldClean($sequence);
3778
        $this->clean($c_sequence);
3779
3780
        $sql = "
3781
			SELECT c.relname AS seqname, s.*,
3782
				pg_catalog.obj_description(s.tableoid, 'pg_class') AS seqcomment,
3783
				u.usename AS seqowner, n.nspname
3784
			FROM \"{$sequence}\" AS s, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
3785
			WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
3786
				AND c.relname = '{$c_sequence}' AND c.relkind = 'S' AND n.nspname='{$c_schema}'
3787
				AND n.oid = c.relnamespace";
3788
3789
        return $this->selectSet($sql);
3790
    }
3791
3792
    /**
3793
     * Creates a new sequence.
3794
     *
3795
     * @param $sequence    Sequence name
3796
     * @param $increment   The increment
3797
     * @param $minvalue    The min value
3798
     * @param $maxvalue    The max value
3799
     * @param $startvalue  The starting value
3800
     * @param $cachevalue  The cache value
3801
     * @param $cycledvalue True if cycled, false otherwise
3802
     *
3803
     * @return \PHPPgAdmin\Database\A 0 success
3804
     */
3805
    public function createSequence(
3806
        $sequence,
3807
        $increment,
3808
        $minvalue,
3809
        $maxvalue,
3810
        $startvalue,
3811
        $cachevalue,
3812
        $cycledvalue
3813
    ) {
3814
        $f_schema = $this->_schema;
3815
        $this->fieldClean($f_schema);
3816
        $this->fieldClean($sequence);
3817
        $this->clean($increment);
3818
        $this->clean($minvalue);
3819
        $this->clean($maxvalue);
3820
        $this->clean($startvalue);
3821
        $this->clean($cachevalue);
3822
3823
        $sql = "CREATE SEQUENCE \"{$f_schema}\".\"{$sequence}\"";
3824
        if ($increment != '') {
3825
            $sql .= " INCREMENT {$increment}";
3826
        }
3827
3828
        if ($minvalue != '') {
3829
            $sql .= " MINVALUE {$minvalue}";
3830
        }
3831
3832
        if ($maxvalue != '') {
3833
            $sql .= " MAXVALUE {$maxvalue}";
3834
        }
3835
3836
        if ($startvalue != '') {
3837
            $sql .= " START {$startvalue}";
3838
        }
3839
3840
        if ($cachevalue != '') {
3841
            $sql .= " CACHE {$cachevalue}";
3842
        }
3843
3844
        if ($cycledvalue) {
3845
            $sql .= ' CYCLE';
3846
        }
3847
3848
        return $this->execute($sql);
3849
    }
3850
3851
    /**
3852
     * Alters a sequence.
3853
     *
3854
     * @param $sequence     The name of the sequence
3855
     * @param $name         The new name for the sequence
3856
     * @param $comment      The comment on the sequence
3857
     * @param $owner        The new owner for the sequence
3858
     * @param $schema       The new schema for the sequence
3859
     * @param $increment    The increment
3860
     * @param $minvalue     The min value
3861
     * @param $maxvalue     The max value
3862
     * @param $restartvalue The starting value
3863
     * @param $cachevalue   The cache value
3864
     * @param $cycledvalue  True if cycled, false otherwise
3865
     * @param $startvalue   The sequence start value when issueing a restart
3866
     *
3867
     * @return bool|int 0 success
3868
     */
3869
    public function alterSequence(
3870
        $sequence,
3871
        $name,
3872
        $comment,
3873
        $owner = null,
3874
        $schema = null,
3875
        $increment = null,
3876
        $minvalue = null,
3877
        $maxvalue = null,
3878
        $restartvalue = null,
3879
        $cachevalue = null,
3880
        $cycledvalue = null,
3881
        $startvalue = null
3882
    ) {
3883
        $this->fieldClean($sequence);
3884
3885
        $data = $this->getSequence($sequence);
3886
3887
        if ($data->recordCount() != 1) {
3888
            return -2;
3889
        }
3890
3891
        $status = $this->beginTransaction();
3892
        if ($status != 0) {
3893
            $this->rollbackTransaction();
3894
3895
            return -1;
3896
        }
3897
3898
        $status = $this->_alterSequence($data, $name, $comment, $owner, $schema, $increment,
3899
            $minvalue, $maxvalue, $restartvalue, $cachevalue, $cycledvalue, $startvalue);
3900
3901
        if ($status != 0) {
3902
            $this->rollbackTransaction();
3903
3904
            return $status;
3905
        }
3906
3907
        return $this->endTransaction();
3908
    }
3909
3910
    /**
3911
     * Protected method which alter a sequence
3912
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
3913
     *
3914
     * @param $seqrs        The sequence recordSet returned by getSequence()
3915
     * @param $name         The new name for the sequence
3916
     * @param $comment      The comment on the sequence
3917
     * @param $owner        The new owner for the sequence
3918
     * @param $schema       The new schema for the sequence
3919
     * @param $increment    The increment
3920
     * @param $minvalue     The min value
3921
     * @param $maxvalue     The max value
3922
     * @param $restartvalue The starting value
3923
     * @param $cachevalue   The cache value
3924
     * @param $cycledvalue  True if cycled, false otherwise
3925
     * @param $startvalue   The sequence start value when issueing a restart
3926
     *
3927
     * @return int 0 success
3928
     */
3929
    protected function _alterSequence(
3930
        $seqrs,
3931
        $name,
3932
        $comment,
3933
        $owner,
3934
        $schema,
3935
        $increment,
3936
        $minvalue,
3937
        $maxvalue,
3938
        $restartvalue,
3939
        $cachevalue,
3940
        $cycledvalue,
3941
        $startvalue
3942
    ) {
3943
        $this->fieldArrayClean($seqrs->fields);
3944
3945
        // Comment
3946
        $status = $this->setComment('SEQUENCE', $seqrs->fields['seqname'], '', $comment);
3947
        if ($status != 0) {
3948
            return -4;
3949
        }
3950
3951
        // Owner
3952
        $this->fieldClean($owner);
3953
        $status = $this->alterSequenceOwner($seqrs, $owner);
3954
        if ($status != 0) {
3955
            return -5;
3956
        }
3957
3958
        // Props
3959
        $this->clean($increment);
3960
        $this->clean($minvalue);
3961
        $this->clean($maxvalue);
3962
        $this->clean($restartvalue);
3963
        $this->clean($cachevalue);
3964
        $this->clean($cycledvalue);
3965
        $this->clean($startvalue);
3966
        $status = $this->alterSequenceProps($seqrs, $increment, $minvalue,
3967
            $maxvalue, $restartvalue, $cachevalue, $cycledvalue, $startvalue);
3968
        if ($status != 0) {
3969
            return -6;
3970
        }
3971
3972
        // Rename
3973
        $this->fieldClean($name);
3974
        $status = $this->alterSequenceName($seqrs, $name);
3975
        if ($status != 0) {
3976
            return -3;
3977
        }
3978
3979
        // Schema
3980
        $this->clean($schema);
3981
        $status = $this->alterSequenceSchema($seqrs, $schema);
3982
        if ($status != 0) {
3983
            return -7;
3984
        }
3985
3986
        return 0;
3987
    }
3988
3989
    // Index functions
3990
3991
    /**
3992
     * Alter a sequence's owner.
3993
     *
3994
     * @param $seqrs The sequence RecordSet returned by getSequence()
3995
     * @param $owner
3996
     *
3997
     * @return int|\PHPPgAdmin\Database\A 0 success
3998
     *
3999
     * @internal param \PHPPgAdmin\Database\The $name new owner for the sequence
4000
     */
4001 View Code Duplication
    public function alterSequenceOwner($seqrs, $owner)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4002
    {
4003
        // If owner has been changed, then do the alteration.  We are
4004
        // careful to avoid this generally as changing owner is a
4005
        // superuser only function.
4006
        /* vars are cleaned in _alterSequence */
4007
        if (!empty($owner) && ($seqrs->fields['seqowner'] != $owner)) {
4008
            $f_schema = $this->_schema;
4009
            $this->fieldClean($f_schema);
4010
            $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" OWNER TO \"{$owner}\"";
4011
4012
            return $this->execute($sql);
4013
        }
4014
4015
        return 0;
4016
    }
4017
4018
    /**
4019
     * Alter a sequence's properties.
4020
     *
4021
     * @param $seqrs        The sequence RecordSet returned by getSequence()
4022
     * @param $increment    The sequence incremental value
4023
     * @param $minvalue     The sequence minimum value
4024
     * @param $maxvalue     The sequence maximum value
4025
     * @param $restartvalue The sequence current value
4026
     * @param $cachevalue   The sequence cache value
4027
     * @param $cycledvalue  Sequence can cycle ?
4028
     * @param $startvalue   The sequence start value when issueing a restart
4029
     *
4030
     * @return int|\PHPPgAdmin\Database\A 0 success
4031
     */
4032
    public function alterSequenceProps(
4033
        $seqrs,
4034
        $increment,
4035
        $minvalue,
4036
        $maxvalue,
4037
        $restartvalue,
4038
        $cachevalue,
4039
        $cycledvalue,
4040
        $startvalue
4041
    ) {
4042
        $sql = '';
4043
        /* vars are cleaned in _alterSequence */
4044
        if (!empty($increment) && ($increment != $seqrs->fields['increment_by'])) {
4045
            $sql .= " INCREMENT {$increment}";
4046
        }
4047
4048
        if (!empty($minvalue) && ($minvalue != $seqrs->fields['min_value'])) {
4049
            $sql .= " MINVALUE {$minvalue}";
4050
        }
4051
4052
        if (!empty($maxvalue) && ($maxvalue != $seqrs->fields['max_value'])) {
4053
            $sql .= " MAXVALUE {$maxvalue}";
4054
        }
4055
4056
        if (!empty($restartvalue) && ($restartvalue != $seqrs->fields['last_value'])) {
4057
            $sql .= " RESTART {$restartvalue}";
4058
        }
4059
4060
        if (!empty($cachevalue) && ($cachevalue != $seqrs->fields['cache_value'])) {
4061
            $sql .= " CACHE {$cachevalue}";
4062
        }
4063
4064
        if (!empty($startvalue) && ($startvalue != $seqrs->fields['start_value'])) {
4065
            $sql .= " START {$startvalue}";
4066
        }
4067
4068
        // toggle cycle yes/no
4069
        if (!is_null($cycledvalue)) {
4070
            $sql .= (!$cycledvalue ? ' NO ' : '').' CYCLE';
4071
        }
4072
4073
        if ($sql != '') {
4074
            $f_schema = $this->_schema;
4075
            $this->fieldClean($f_schema);
4076
            $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" {$sql}";
4077
4078
            return $this->execute($sql);
4079
        }
4080
4081
        return 0;
4082
    }
4083
4084
    /**
4085
     * Rename a sequence.
4086
     *
4087
     * @param $seqrs The sequence RecordSet returned by getSequence()
4088
     * @param $name  The new name for the sequence
4089
     *
4090
     * @return int|\PHPPgAdmin\Database\A 0 success
4091
     */
4092 View Code Duplication
    public function alterSequenceName($seqrs, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4093
    {
4094
        /* vars are cleaned in _alterSequence */
4095
        if (!empty($name) && ($seqrs->fields['seqname'] != $name)) {
4096
            $f_schema = $this->_schema;
4097
            $this->fieldClean($f_schema);
4098
            $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" RENAME TO \"{$name}\"";
4099
            $status = $this->execute($sql);
4100
            if ($status == 0) {
4101
                $seqrs->fields['seqname'] = $name;
4102
            } else {
4103
                return $status;
4104
            }
4105
        }
4106
4107
        return 0;
4108
    }
4109
4110
    /**
4111
     * Alter a sequence's schema.
4112
     *
4113
     * @param $seqrs The sequence RecordSet returned by getSequence()
4114
     * @param $schema
4115
     *
4116
     * @return int|\PHPPgAdmin\Database\A 0 success
4117
     *
4118
     * @internal param \PHPPgAdmin\Database\The $name new schema for the sequence
4119
     */
4120 View Code Duplication
    public function alterSequenceSchema($seqrs, $schema)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4121
    {
4122
        /* vars are cleaned in _alterSequence */
4123
        if (!empty($schema) && ($seqrs->fields['nspname'] != $schema)) {
4124
            $f_schema = $this->_schema;
4125
            $this->fieldClean($f_schema);
4126
            $sql = "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" SET SCHEMA {$schema}";
4127
4128
            return $this->execute($sql);
4129
        }
4130
4131
        return 0;
4132
    }
4133
4134
    /**
4135
     * Drops a given sequence.
4136
     *
4137
     * @param $sequence Sequence name
4138
     * @param $cascade  True to cascade drop, false to restrict
4139
     *
4140
     * @return \PHPPgAdmin\Database\A 0 success
4141
     */
4142 View Code Duplication
    public function dropSequence($sequence, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4143
    {
4144
        $f_schema = $this->_schema;
4145
        $this->fieldClean($f_schema);
4146
        $this->fieldClean($sequence);
4147
4148
        $sql = "DROP SEQUENCE \"{$f_schema}\".\"{$sequence}\"";
4149
        if ($cascade) {
4150
            $sql .= ' CASCADE';
4151
        }
4152
4153
        return $this->execute($sql);
4154
    }
4155
4156
    /**
4157
     * Returns a list of all views in the database.
4158
     *
4159
     * @return All views
4160
     */
4161
    public function getViews()
4162
    {
4163
        $c_schema = $this->_schema;
4164
        $this->clean($c_schema);
4165
        $sql = "
4166
			SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
4167
				pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
4168
			FROM pg_catalog.pg_class c
4169
				LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
4170
			WHERE (n.nspname='{$c_schema}') AND (c.relkind = 'v'::\"char\")
4171
			ORDER BY relname";
4172
4173
        return $this->selectSet($sql);
4174
    }
4175
4176
    // Constraint functions
4177
4178
    /**
4179
     * Returns a list of all views in the database.
4180
     *
4181
     * @return All views
4182
     */
4183
    public function getMaterializedViews()
4184
    {
4185
        $c_schema = $this->_schema;
4186
        $this->clean($c_schema);
4187
        $sql = "
4188
			SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
4189
				pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
4190
			FROM pg_catalog.pg_class c
4191
				LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
4192
			WHERE (n.nspname='{$c_schema}') AND (c.relkind = 'm'::\"char\")
4193
			ORDER BY relname";
4194
4195
        return $this->selectSet($sql);
4196
    }
4197
4198
    /**
4199
     * Updates a view.
4200
     *
4201
     * @param $viewname   The name fo the view to update
4202
     * @param $definition The new definition for the view
4203
     * @param $comment
4204
     *
4205
     * @return bool|int 0 success
4206
     */
4207
    public function setView($viewname, $definition, $comment)
4208
    {
4209
        return $this->createView($viewname, $definition, true, $comment);
4210
    }
4211
4212
    /**
4213
     * Creates a new view.
4214
     *
4215
     * @param $viewname   The name of the view to create
4216
     * @param $definition The definition for the new view
4217
     * @param $replace    True to replace the view, false otherwise
4218
     * @param $comment
4219
     *
4220
     * @return bool|int 0 success
4221
     */
4222
    public function createView($viewname, $definition, $replace, $comment)
4223
    {
4224
        $status = $this->beginTransaction();
4225
        if ($status != 0) {
4226
            return -1;
4227
        }
4228
4229
        $f_schema = $this->_schema;
4230
        $this->fieldClean($f_schema);
4231
        $this->fieldClean($viewname);
4232
4233
        // Note: $definition not cleaned
4234
4235
        $sql = 'CREATE ';
4236
        if ($replace) {
4237
            $sql .= 'OR REPLACE ';
4238
        }
4239
4240
        $sql .= "VIEW \"{$f_schema}\".\"{$viewname}\" AS {$definition}";
4241
4242
        $status = $this->execute($sql);
4243
        if ($status) {
4244
            $this->rollbackTransaction();
4245
4246
            return -1;
4247
        }
4248
4249
        if ($comment != '') {
4250
            $status = $this->setComment('VIEW', $viewname, '', $comment);
4251
            if ($status) {
4252
                $this->rollbackTransaction();
4253
4254
                return -1;
4255
            }
4256
        }
4257
4258
        return $this->endTransaction();
4259
    }
4260
4261
    /**
4262
     * Alter view properties.
4263
     *
4264
     * @param $view    The name of the view
4265
     * @param $name    The new name for the view
4266
     * @param $owner   The new owner for the view
4267
     * @param $schema  The new schema for the view
4268
     * @param $comment The comment on the view
4269
     *
4270
     * @return bool|int 0 success
4271
     */
4272 View Code Duplication
    public function alterView($view, $name, $owner, $schema, $comment)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4273
    {
4274
        $data = $this->getView($view);
4275
        if ($data->recordCount() != 1) {
4276
            return -2;
4277
        }
4278
4279
        $status = $this->beginTransaction();
4280
        if ($status != 0) {
4281
            $this->rollbackTransaction();
4282
4283
            return -1;
4284
        }
4285
4286
        $status = $this->_alterView($data, $name, $owner, $schema, $comment);
4287
4288
        if ($status != 0) {
4289
            $this->rollbackTransaction();
4290
4291
            return $status;
4292
        }
4293
4294
        return $this->endTransaction();
4295
    }
4296
4297
    /**
4298
     * Returns all details for a particular view.
4299
     *
4300
     * @param $view The name of the view to retrieve
4301
     *
4302
     * @return View info
4303
     */
4304
    public function getView($view)
4305
    {
4306
        $c_schema = $this->_schema;
4307
        $this->clean($c_schema);
4308
        $this->clean($view);
4309
4310
        $sql = "
4311
			SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
4312
				pg_catalog.pg_get_viewdef(c.oid, true) AS vwdefinition,
4313
				pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
4314
			FROM pg_catalog.pg_class c
4315
				LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
4316
			WHERE (c.relname = '{$view}') AND n.nspname='{$c_schema}'";
4317
4318
        return $this->selectSet($sql);
4319
    }
4320
4321
    /**
4322
     * Protected method which alter a view
4323
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION.
4324
     *
4325
     * @param $vwrs    The view recordSet returned by getView()
4326
     * @param $name    The new name for the view
4327
     * @param $owner   The new owner for the view
4328
     * @param $schema
4329
     * @param $comment The comment on the view
4330
     *
4331
     * @return int 0 success
4332
     */
4333 View Code Duplication
    protected function _alterView($vwrs, $name, $owner, $schema, $comment)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4334
    {
4335
        $this->fieldArrayClean($vwrs->fields);
4336
4337
        // Comment
4338
        if ($this->setComment('VIEW', $vwrs->fields['relname'], '', $comment) != 0) {
4339
            return -4;
4340
        }
4341
4342
        // Owner
4343
        $this->fieldClean($owner);
4344
        $status = $this->alterViewOwner($vwrs, $owner);
4345
        if ($status != 0) {
4346
            return -5;
4347
        }
4348
4349
        // Rename
4350
        $this->fieldClean($name);
4351
        $status = $this->alterViewName($vwrs, $name);
4352
        if ($status != 0) {
4353
            return -3;
4354
        }
4355
4356
        // Schema
4357
        $this->fieldClean($schema);
4358
        $status = $this->alterViewSchema($vwrs, $schema);
4359
        if ($status != 0) {
4360
            return -6;
4361
        }
4362
4363
        return 0;
4364
    }
4365
4366
    /**
4367
     * Alter a view's owner.
4368
     *
4369
     * @param      $vwrs  The view recordSet returned by getView()
4370
     * @param null $owner
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $owner is correct as it would always require null to be passed?
Loading history...
4371
     *
4372
     * @return int|\PHPPgAdmin\Database\A 0 success
4373
     *
4374
     * @internal param \PHPPgAdmin\Database\The $name new view's owner
4375
     */
4376 View Code Duplication
    public function alterViewOwner($vwrs, $owner = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4377
    {
4378
        /* $vwrs and $owner are cleaned in _alterView */
4379
        if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) {
4380
            $f_schema = $this->_schema;
4381
            $this->fieldClean($f_schema);
4382
            // If owner has been changed, then do the alteration.  We are
4383
            // careful to avoid this generally as changing owner is a
4384
            // superuser only function.
4385
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$vwrs->fields['relname']}\" OWNER TO \"{$owner}\"";
4386
4387
            return $this->execute($sql);
4388
        }
4389
4390
        return 0;
4391
    }
4392
4393
    /**
4394
     * Rename a view.
4395
     *
4396
     * @param $vwrs The view recordSet returned by getView()
4397
     * @param $name The new view's name
4398
     *
4399
     * @return int|\PHPPgAdmin\Database\A 0 success
4400
     */
4401 View Code Duplication
    public function alterViewName($vwrs, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4402
    {
4403
        // Rename (only if name has changed)
4404
        /* $vwrs and $name are cleaned in _alterView */
4405
        if (!empty($name) && ($name != $vwrs->fields['relname'])) {
4406
            $f_schema = $this->_schema;
4407
            $this->fieldClean($f_schema);
4408
            $sql = "ALTER VIEW \"{$f_schema}\".\"{$vwrs->fields['relname']}\" RENAME TO \"{$name}\"";
4409
            $status = $this->execute($sql);
4410
            if ($status == 0) {
4411
                $vwrs->fields['relname'] = $name;
4412
            } else {
4413
                return $status;
4414
            }
4415
        }
4416
4417
        return 0;
4418
    }
4419
4420
    /**
4421
     * Alter a view's schema.
4422
     *
4423
     * @param $vwrs The view recordSet returned by getView()
4424
     * @param $schema
4425
     *
4426
     * @return int|\PHPPgAdmin\Database\A 0 success
4427
     *
4428
     * @internal param \PHPPgAdmin\Database\The $name new view's schema
4429
     */
4430 View Code Duplication
    public function alterViewSchema($vwrs, $schema)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4431
    {
4432
        /* $vwrs and $schema are cleaned in _alterView */
4433
        if (!empty($schema) && ($vwrs->fields['nspname'] != $schema)) {
4434
            $f_schema = $this->_schema;
4435
            $this->fieldClean($f_schema);
4436
            // If tablespace has been changed, then do the alteration.  We
4437
            // don't want to do this unnecessarily.
4438
            $sql = "ALTER TABLE \"{$f_schema}\".\"{$vwrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
4439
4440
            return $this->execute($sql);
4441
        }
4442
4443
        return 0;
4444
    }
4445
4446
    /**
4447
     * Drops a view.
4448
     *
4449
     * @param $viewname The name of the view to drop
4450
     * @param $cascade  True to cascade drop, false to restrict
4451
     *
4452
     * @return \PHPPgAdmin\Database\A 0 success
4453
     */
4454 View Code Duplication
    public function dropView($viewname, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4455
    {
4456
        $f_schema = $this->_schema;
4457
        $this->fieldClean($f_schema);
4458
        $this->fieldClean($viewname);
4459
4460
        $sql = "DROP VIEW \"{$f_schema}\".\"{$viewname}\"";
4461
        if ($cascade) {
4462
            $sql .= ' CASCADE';
4463
        }
4464
4465
        return $this->execute($sql);
4466
    }
4467
4468
    // Domain functions
4469
4470
    /**
4471
     * test if a table has been clustered on an index.
4472
     *
4473
     * @param $table The table to test
4474
     *
4475
     * @return true if the table has been already clustered
4476
     */
4477 View Code Duplication
    public function alreadyClustered($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4478
    {
4479
        $c_schema = $this->_schema;
4480
        $this->clean($c_schema);
4481
        $this->clean($table);
4482
4483
        $sql = "SELECT i.indisclustered
4484
			FROM pg_catalog.pg_class c, pg_catalog.pg_index i
4485
			WHERE c.relname = '{$table}'
4486
				AND c.oid = i.indrelid AND i.indisclustered
4487
				AND c.relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
4488
					WHERE nspname='{$c_schema}')
4489
				";
4490
4491
        $v = $this->selectSet($sql);
4492
4493
        return !($v->recordCount() == 0);
4494
    }
4495
4496
    /**
4497
     * Creates an index.
4498
     *
4499
     * @param $name       The index name
4500
     * @param $table      The table on which to add the index
4501
     * @param $columns    An array of columns that form the index
4502
     *                    or a string expression for a functional index
4503
     * @param $type       The index type
4504
     * @param $unique     True if unique, false otherwise
4505
     * @param $where      Index predicate ('' for none)
4506
     * @param $tablespace The tablespaces ('' means none/default)
4507
     * @param $concurrently
4508
     *
4509
     * @return \PHPPgAdmin\Database\A 0 success
4510
     */
4511
    public function createIndex($name, $table, $columns, $type, $unique, $where, $tablespace, $concurrently)
4512
    {
4513
        $f_schema = $this->_schema;
4514
        $this->fieldClean($f_schema);
4515
        $this->fieldClean($name);
4516
        $this->fieldClean($table);
4517
4518
        $sql = 'CREATE';
4519
        if ($unique) {
4520
            $sql .= ' UNIQUE';
4521
        }
4522
4523
        $sql .= ' INDEX';
4524
        if ($concurrently) {
4525
            $sql .= ' CONCURRENTLY';
4526
        }
4527
4528
        $sql .= " \"{$name}\" ON \"{$f_schema}\".\"{$table}\" USING {$type} ";
4529
4530
        if (is_array($columns)) {
4531
            $this->arrayClean($columns);
4532
            $sql .= '("'.implode('","', $columns).'")';
4533
        } else {
4534
            $sql .= '('.$columns.')';
4535
        }
4536
4537
        // Tablespace
4538
        if ($this->hasTablespaces() && $tablespace != '') {
4539
            $this->fieldClean($tablespace);
4540
            $sql .= " TABLESPACE \"{$tablespace}\"";
4541
        }
4542
4543
        // Predicate
4544
        if (trim($where) != '') {
4545
            $sql .= " WHERE ({$where})";
4546
        }
4547
4548
        return $this->execute($sql);
4549
    }
4550
4551
    /**
4552
     * Removes an index from the database.
4553
     *
4554
     * @param $index   The index to drop
4555
     * @param $cascade True to cascade drop, false to restrict
4556
     *
4557
     * @return \PHPPgAdmin\Database\A 0 success
4558
     */
4559 View Code Duplication
    public function dropIndex($index, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4560
    {
4561
        $f_schema = $this->_schema;
4562
        $this->fieldClean($f_schema);
4563
        $this->fieldClean($index);
4564
4565
        $sql = "DROP INDEX \"{$f_schema}\".\"{$index}\"";
4566
        if ($cascade) {
4567
            $sql .= ' CASCADE';
4568
        }
4569
4570
        return $this->execute($sql);
4571
    }
4572
4573
    /**
4574
     * Rebuild indexes.
4575
     *
4576
     * @param                              $type  'DATABASE' or 'TABLE' or 'INDEX'
4577
     * @param                              $name  The name of the specific database, table, or index to be reindexed
4578
     * @param bool|\PHPPgAdmin\Database\If $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in
4579
     *                                            7.2-7.3, ignored in >=7.4
4580
     *
4581
     * @return int|\PHPPgAdmin\Database\A
4582
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'DATABASE' at position 0 could not be parsed: Unknown type name ''DATABASE'' at position 0 in 'DATABASE'.
Loading history...
4583
    public function reindex($type, $name, $force = false)
4584
    {
4585
        $f_schema = $this->_schema;
4586
        $this->fieldClean($f_schema);
4587
        $this->fieldClean($name);
4588
        switch ($type) {
4589
            case 'DATABASE':
4590
                $sql = "REINDEX {$type} \"{$name}\"";
4591
                if ($force) {
4592
                    $sql .= ' FORCE';
4593
                }
4594
4595
                break;
4596
            case 'TABLE':
4597
            case 'INDEX':
4598
                $sql = "REINDEX {$type} \"{$f_schema}\".\"{$name}\"";
4599
                if ($force) {
4600
                    $sql .= ' FORCE';
4601
                }
4602
4603
                break;
4604
            default:
4605
                return -1;
4606
        }
4607
4608
        return $this->execute($sql);
4609
    }
4610
4611
    /**
4612
     * Clusters an index.
4613
     *
4614
     * @param \PHPPgAdmin\Database\The|string $table The table the index is on
4615
     * @param \PHPPgAdmin\Database\The|string $index The name of the index
4616
     *
4617
     * @return \PHPPgAdmin\Database\A 0 success
4618
     */
4619 View Code Duplication
    public function clusterIndex($table = '', $index = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4620
    {
4621
        $sql = 'CLUSTER';
4622
4623
        // We don't bother with a transaction here, as there's no point rolling
4624
        // back an expensive cluster if a cheap analyze fails for whatever reason
4625
4626
        if (!empty($table)) {
4627
            $f_schema = $this->_schema;
4628
            $this->fieldClean($f_schema);
4629
            $this->fieldClean($table);
4630
            $sql .= " \"{$f_schema}\".\"{$table}\"";
4631
4632
            if (!empty($index)) {
4633
                $this->fieldClean($index);
4634
                $sql .= " USING \"{$index}\"";
4635
            }
4636
        }
4637
4638
        return $this->execute($sql);
4639
    }
4640
4641
    /**
4642
     * Returns a list of all constraints on a table,
4643
     * including constraint name, definition, related col and referenced namespace,
4644
     * table and col if needed.
4645
     *
4646
     * @param $table the table where we are looking for fk
4647
     *
4648
     * @return a recordset
4649
     */
4650 View Code Duplication
    public function getConstraintsWithFields($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4651
    {
4652
        $c_schema = $this->_schema;
4653
        $this->clean($c_schema);
4654
        $this->clean($table);
4655
4656
        // get the max number of col used in a constraint for the table
4657
        $sql = "SELECT DISTINCT
4658
			max(SUBSTRING(array_dims(c.conkey) FROM  \$patern\$^\\[.*:(.*)\\]$\$patern\$)) as nb
4659
		FROM pg_catalog.pg_constraint AS c
4660
			JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid)
4661
			JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid)
4662
		WHERE
4663
			r.relname = '{$table}' AND ns.nspname='{$c_schema}'";
4664
4665
        $rs = $this->selectSet($sql);
4666
4667
        if ($rs->EOF) {
4668
            $max_col = 0;
4669
        } else {
4670
            $max_col = $rs->fields['nb'];
4671
        }
4672
4673
        $sql = '
4674
			SELECT
4675
				c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
4676
				ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
4677
				r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field,
4678
				f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
4679
				c.conrelid, c.confrelid
4680
			FROM
4681
				pg_catalog.pg_constraint AS c
4682
				JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
4683
				JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
4684
        for ($i = 2; $i <= $rs->fields['nb']; $i++) {
4685
            $sql .= " OR f1.attnum=c.conkey[$i]";
4686
        }
4687
        $sql .= '))
4688
				JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
4689
				LEFT JOIN (
4690
					pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
4691
				) ON (c.confrelid=r2.oid)
4692
				LEFT JOIN pg_catalog.pg_attribute AS f2 ON
4693
					(f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
4694
        for ($i = 2; $i <= $rs->fields['nb']; $i++) {
4695
            $sql .= " OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
4696
        }
4697
4698
        $sql .= sprintf("))
4699
			WHERE
4700
				r1.relname = '%s' AND ns1.nspname='%s'
4701
			ORDER BY 1", $table, $c_schema);
4702
4703
        return $this->selectSet($sql);
4704
    }
4705
4706
    /**
4707
     * Adds a primary key constraint to a table.
4708
     *
4709
     * @param        $table      The table to which to add the primery key
4710
     * @param        $fields     (array) An array of fields over which to add the primary key
4711
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
4712
     * @param string $tablespace (optional) The tablespace for the schema, '' indicates default.
4713
     *
4714
     * @return int|\PHPPgAdmin\Database\A 0 success
4715
     */
4716 View Code Duplication
    public function addPrimaryKey($table, $fields, $name = '', $tablespace = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4717
    {
4718
        if (!is_array($fields) || count($fields) == 0) {
4719
            return -1;
4720
        }
4721
4722
        $f_schema = $this->_schema;
4723
        $this->fieldClean($f_schema);
4724
        $this->fieldClean($table);
4725
        $this->fieldArrayClean($fields);
4726
        $this->fieldClean($name);
4727
        $this->fieldClean($tablespace);
4728
4729
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
4730
        if ($name != '') {
4731
            $sql .= "CONSTRAINT \"{$name}\" ";
4732
        }
4733
4734
        $sql .= 'PRIMARY KEY ("'.implode('","', $fields).'")';
4735
4736
        if ($tablespace != '' && $this->hasTablespaces()) {
4737
            $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
4738
        }
4739
4740
        return $this->execute($sql);
4741
    }
4742
4743
    /**
4744
     * Adds a unique constraint to a table.
4745
     *
4746
     * @param        $table      The table to which to add the unique key
4747
     * @param        $fields     (array) An array of fields over which to add the unique key
4748
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
4749
     * @param string $tablespace (optional) The tablespace for the schema, '' indicates default.
4750
     *
4751
     * @return int|\PHPPgAdmin\Database\A 0 success
4752
     */
4753 View Code Duplication
    public function addUniqueKey($table, $fields, $name = '', $tablespace = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4754
    {
4755
        if (!is_array($fields) || count($fields) == 0) {
4756
            return -1;
4757
        }
4758
4759
        $f_schema = $this->_schema;
4760
        $this->fieldClean($f_schema);
4761
        $this->fieldClean($table);
4762
        $this->fieldArrayClean($fields);
4763
        $this->fieldClean($name);
4764
        $this->fieldClean($tablespace);
4765
4766
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
4767
        if ($name != '') {
4768
            $sql .= "CONSTRAINT \"{$name}\" ";
4769
        }
4770
4771
        $sql .= 'UNIQUE ("'.implode('","', $fields).'")';
4772
4773
        if ($tablespace != '' && $this->hasTablespaces()) {
4774
            $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
4775
        }
4776
4777
        return $this->execute($sql);
4778
    }
4779
4780
    // Function functions
4781
4782
    /**
4783
     * Adds a check constraint to a table.
4784
     *
4785
     * @param        $table      The table to which to add the check
4786
     * @param        $definition The definition of the check
4787
     * @param string $name       (optional) The name to give the check, otherwise default name is assigned
4788
     *
4789
     * @return \PHPPgAdmin\Database\A 0 success
4790
     */
4791 View Code Duplication
    public function addCheckConstraint($table, $definition, $name = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4792
    {
4793
        $f_schema = $this->_schema;
4794
        $this->fieldClean($f_schema);
4795
        $this->fieldClean($table);
4796
        $this->fieldClean($name);
4797
        // @@ How the heck do you clean a definition???
4798
4799
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
4800
        if ($name != '') {
4801
            $sql .= "CONSTRAINT \"{$name}\" ";
4802
        }
4803
4804
        $sql .= "CHECK ({$definition})";
4805
4806
        return $this->execute($sql);
4807
    }
4808
4809
    /**
4810
     * Drops a check constraint from a table.
4811
     *
4812
     * @param $table The table from which to drop the check
4813
     * @param $name  The name of the check to be dropped
4814
     *
4815
     * @return bool|int 0 success
4816
     */
4817
    public function dropCheckConstraint($table, $name)
4818
    {
4819
        $f_schema = $this->_schema;
4820
        $this->fieldClean($f_schema);
4821
        $c_schema = $this->_schema;
4822
        $this->clean($c_schema);
4823
        $c_table = $table;
4824
        $this->fieldClean($table);
4825
        $this->clean($c_table);
4826
        $this->clean($name);
4827
4828
        // Begin transaction
4829
        $status = $this->beginTransaction();
4830
        if ($status != 0) {
4831
            return -2;
4832
        }
4833
4834
        // Properly lock the table
4835
        $sql = "LOCK TABLE \"{$f_schema}\".\"{$table}\" IN ACCESS EXCLUSIVE MODE";
4836
        $status = $this->execute($sql);
4837
        if ($status != 0) {
4838
            $this->rollbackTransaction();
4839
4840
            return -3;
4841
        }
4842
4843
        // Delete the check constraint
4844
        $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}'
4845
			AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
4846
			nspname = '{$c_schema}')) AND rcname='{$name}'";
4847
        $status = $this->execute($sql);
4848
        if ($status != 0) {
4849
            $this->rollbackTransaction();
4850
4851
            return -4;
4852
        }
4853
4854
        // Update the pg_class catalog to reflect the new number of checks
4855
        $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
4856
					rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$c_table}'
4857
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
4858
						nspname = '{$c_schema}')))
4859
					WHERE relname='{$c_table}'";
4860
        $status = $this->execute($sql);
4861
        if ($status != 0) {
4862
            $this->rollbackTransaction();
4863
4864
            return -4;
4865
        }
4866
4867
        // Otherwise, close the transaction
4868
        return $this->endTransaction();
4869
    }
4870
4871
    /**
4872
     * Adds a foreign key constraint to a table.
4873
     *
4874
     * @param        $table
4875
     * @param        $targschema The schema that houses the target table to which to add the foreign key
4876
     * @param        $targtable  The table to which to add the foreign key
4877
     * @param        $sfields    (array) An array of source fields over which to add the foreign key
4878
     * @param        $tfields    (array) An array of target fields over which to add the foreign key
4879
     * @param        $upd_action The action for updates (eg. RESTRICT)
4880
     * @param        $del_action The action for deletes (eg. RESTRICT)
4881
     * @param        $match      The match type (eg. MATCH FULL)
4882
     * @param        $deferrable The deferrability (eg. NOT DEFERRABLE)
4883
     * @param        $initially
4884
     * @param string $name       (optional) The name to give the key, otherwise default name is assigned
4885
     *
4886
     * @return \PHPPgAdmin\Database\A 0 success
4887
     *
4888
     * @internal param \PHPPgAdmin\Database\The $target table that contains the target columns
4889
     * @internal param \PHPPgAdmin\Database\The $intially initial deferrability (eg. INITIALLY IMMEDIATE)
4890
     */
4891
    public function addForeignKey(
4892
        $table,
4893
        $targschema,
4894
        $targtable,
4895
        $sfields,
4896
        $tfields,
4897
        $upd_action,
4898
        $del_action,
4899
        $match,
4900
        $deferrable,
4901
        $initially,
4902
        $name = ''
4903
    ) {
4904
        if (!is_array($sfields) || count($sfields) == 0 ||
4905
            !is_array($tfields) || count($tfields) == 0) {
4906
            return -1;
4907
        }
4908
4909
        $f_schema = $this->_schema;
4910
        $this->fieldClean($f_schema);
4911
        $this->fieldClean($table);
4912
        $this->fieldClean($targschema);
4913
        $this->fieldClean($targtable);
4914
        $this->fieldArrayClean($sfields);
4915
        $this->fieldArrayClean($tfields);
4916
        $this->fieldClean($name);
4917
4918
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
4919
        if ($name != '') {
4920
            $sql .= "CONSTRAINT \"{$name}\" ";
4921
        }
4922
4923
        $sql .= 'FOREIGN KEY ("'.implode('","', $sfields).'") ';
4924
        // Target table needs to be fully qualified
4925
        $sql .= "REFERENCES \"{$targschema}\".\"{$targtable}\"(\"".implode('","', $tfields).'") ';
4926
        if ($match != $this->fkmatches[0]) {
4927
            $sql .= " {$match}";
4928
        }
4929
4930
        if ($upd_action != $this->fkactions[0]) {
4931
            $sql .= " ON UPDATE {$upd_action}";
4932
        }
4933
4934
        if ($del_action != $this->fkactions[0]) {
4935
            $sql .= " ON DELETE {$del_action}";
4936
        }
4937
4938
        if ($deferrable != $this->fkdeferrable[0]) {
4939
            $sql .= " {$deferrable}";
4940
        }
4941
4942
        if ($initially != $this->fkinitial[0]) {
4943
            $sql .= " {$initially}";
4944
        }
4945
4946
        return $this->execute($sql);
4947
    }
4948
4949
    /**
4950
     * Removes a constraint from a relation.
4951
     *
4952
     * @param $constraint The constraint to drop
4953
     * @param $relation   The relation from which to drop
4954
     * @param $type       The type of constraint (c, f, u or p)
4955
     * @param $cascade    True to cascade drop, false to restrict
4956
     *
4957
     * @return \PHPPgAdmin\Database\A 0 success
4958
     */
4959 View Code Duplication
    public function dropConstraint($constraint, $relation, $type, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
4960
    {
4961
        $f_schema = $this->_schema;
4962
        $this->fieldClean($f_schema);
4963
        $this->fieldClean($constraint);
4964
        $this->fieldClean($relation);
4965
4966
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
4967
        if ($cascade) {
4968
            $sql .= ' CASCADE';
4969
        }
4970
4971
        return $this->execute($sql);
4972
    }
4973
4974
    /**
4975
     * A function for getting all columns linked by foreign keys given a group of tables.
4976
     *
4977
     * @param $tables multi dimensional assoc array that holds schema and table name
4978
     *
4979
     * @return A  recordset of linked tables and columns
4980
     * @return -1 $tables isn't an array
4981
     */
4982
    public function getLinkingKeys($tables)
4983
    {
4984
        if (!is_array($tables)) {
4985
            return -1;
4986
        }
4987
4988
        $this->clean($tables[0]['tablename']);
4989
        $this->clean($tables[0]['schemaname']);
4990
        $tables_list = "'{$tables[0]['tablename']}'";
4991
        $schema_list = "'{$tables[0]['schemaname']}'";
4992
        $schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'";
4993
4994
        for ($i = 1; $i < count($tables); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
4995
            $this->clean($tables[$i]['tablename']);
4996
            $this->clean($tables[$i]['schemaname']);
4997
            $tables_list .= ", '{$tables[$i]['tablename']}'";
4998
            $schema_list .= ", '{$tables[$i]['schemaname']}'";
4999
            $schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'";
5000
        }
5001
5002
        $maxDimension = 1;
5003
5004
        $sql = "
5005
			SELECT DISTINCT
5006
				array_dims(pc.conkey) AS arr_dim,
5007
				pgc1.relname AS p_table
5008
			FROM
5009
				pg_catalog.pg_constraint AS pc,
5010
				pg_catalog.pg_class AS pgc1
5011
			WHERE
5012
				pc.contype = 'f'
5013
				AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
5014
				AND pgc1.relname IN ($tables_list)
5015
			";
5016
5017
        //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
5018
        $rs = $this->selectSet($sql);
5019
        while (!$rs->EOF) {
5020
            $arrData = explode(':', $rs->fields['arr_dim']);
5021
            $tmpDimension = intval(substr($arrData[1], 0, strlen($arrData[1] - 1)));
5022
            $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
5023
            $rs->MoveNext();
5024
        }
5025
5026
        //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
5027
        $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
5028
        for ($i = 2; $i <= $maxDimension; $i++) {
5029
            $cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) ";
5030
        }
5031
        $cons_str .= ') ';
5032
5033
        $sql = "
5034
			SELECT
5035
				pgc1.relname AS p_table,
5036
				pgc2.relname AS f_table,
5037
				pfield.attname AS p_field,
5038
				cfield.attname AS f_field,
5039
				pgns1.nspname AS p_schema,
5040
				pgns2.nspname AS f_schema
5041
			FROM
5042
				pg_catalog.pg_constraint AS pc,
5043
				pg_catalog.pg_class AS pgc1,
5044
				pg_catalog.pg_class AS pgc2,
5045
				pg_catalog.pg_attribute AS pfield,
5046
				pg_catalog.pg_attribute AS cfield,
5047
				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns1,
5048
 				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns2
5049
			WHERE
5050
				pc.contype = 'f'
5051
				AND pgc1.relnamespace = pgns1.ns_id
5052
 				AND pgc2.relnamespace = pgns2.ns_id
5053
				AND pc.conrelid = pgc1.relfilenode
5054
				AND pc.confrelid = pgc2.relfilenode
5055
				AND pfield.attrelid = pc.conrelid
5056
				AND cfield.attrelid = pc.confrelid
5057
				AND $cons_str
5058
				AND pgns1.nspname || '.' || pgc1.relname IN ($schema_tables_list)
5059
				AND pgns2.nspname || '.' || pgc2.relname IN ($schema_tables_list)
5060
		";
5061
5062
        return $this->selectSet($sql);
5063
    }
5064
5065
    /**
5066
     * Finds the foreign keys that refer to the specified table.
5067
     *
5068
     * @param $table The table to find referrers for
5069
     *
5070
     * @return A recordset
5071
     */
5072 View Code Duplication
    public function getReferrers($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5073
    {
5074
        $this->clean($table);
5075
5076
        $status = $this->beginTransaction();
5077
        if ($status != 0) {
5078
            return -1;
5079
        }
5080
5081
        $c_schema = $this->_schema;
5082
        $this->clean($c_schema);
5083
5084
        $sql = "
5085
			SELECT
5086
				pn.nspname,
5087
				pl.relname,
5088
				pc.conname,
5089
				pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
5090
			FROM
5091
				pg_catalog.pg_constraint pc,
5092
				pg_catalog.pg_namespace pn,
5093
				pg_catalog.pg_class pl
5094
			WHERE
5095
				pc.connamespace = pn.oid
5096
				AND pc.conrelid = pl.oid
5097
				AND pc.contype = 'f'
5098
				AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
5099
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
5100
					WHERE nspname='{$c_schema}'))
5101
			ORDER BY 1,2,3
5102
		";
5103
5104
        return $this->selectSet($sql);
5105
    }
5106
5107
    // Type functions
5108
5109
    /**
5110
     * Gets all information for a single domain.
5111
     *
5112
     * @param $domain The name of the domain to fetch
5113
     *
5114
     * @return A recordset
5115
     */
5116
    public function getDomain($domain)
5117
    {
5118
        $c_schema = $this->_schema;
5119
        $this->clean($c_schema);
5120
        $this->clean($domain);
5121
5122
        $sql = "
5123
			SELECT
5124
				t.typname AS domname,
5125
				pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
5126
				t.typnotnull AS domnotnull,
5127
				t.typdefault AS domdef,
5128
				pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
5129
				pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
5130
			FROM
5131
				pg_catalog.pg_type t
5132
			WHERE
5133
				t.typtype = 'd'
5134
				AND t.typname = '{$domain}'
5135
				AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
5136
					WHERE nspname = '{$c_schema}')";
5137
5138
        return $this->selectSet($sql);
5139
    }
5140
5141
    /**
5142
     * Return all domains in current schema.  Excludes domain constraints.
5143
     *
5144
     * @return All tables, sorted alphabetically
5145
     */
5146
    public function getDomains()
5147
    {
5148
        $c_schema = $this->_schema;
5149
        $this->clean($c_schema);
5150
5151
        $sql = "
5152
			SELECT
5153
				t.typname AS domname,
5154
				pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
5155
				t.typnotnull AS domnotnull,
5156
				t.typdefault AS domdef,
5157
				pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
5158
				pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
5159
			FROM
5160
				pg_catalog.pg_type t
5161
			WHERE
5162
				t.typtype = 'd'
5163
				AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
5164
					WHERE nspname='{$c_schema}')
5165
			ORDER BY t.typname";
5166
5167
        return $this->selectSet($sql);
5168
    }
5169
5170
    /**
5171
     * Get domain constraints.
5172
     *
5173
     * @param $domain The name of the domain whose constraints to fetch
5174
     *
5175
     * @return A recordset
5176
     */
5177
    public function getDomainConstraints($domain)
5178
    {
5179
        $c_schema = $this->_schema;
5180
        $this->clean($c_schema);
5181
        $this->clean($domain);
5182
5183
        $sql = "
5184
			SELECT
5185
				conname,
5186
				contype,
5187
				pg_catalog.pg_get_constraintdef(oid, true) AS consrc
5188
			FROM
5189
				pg_catalog.pg_constraint
5190
			WHERE
5191
				contypid = (
5192
					SELECT oid FROM pg_catalog.pg_type
5193
					WHERE typname='{$domain}'
5194
						AND typnamespace = (
5195
							SELECT oid FROM pg_catalog.pg_namespace
5196
							WHERE nspname = '{$c_schema}')
5197
				)
5198
			ORDER BY conname";
5199
5200
        return $this->selectSet($sql);
5201
    }
5202
5203
    /**
5204
     * Creates a domain.
5205
     *
5206
     * @param $domain  The name of the domain to create
5207
     * @param $type    The base type for the domain
5208
     * @param $length  Optional type length
5209
     * @param $array   True for array type, false otherwise
5210
     * @param $notnull True for NOT NULL, false otherwise
5211
     * @param $default Default value for domain
5212
     * @param $check   A CHECK constraint if there is one
5213
     *
5214
     * @return \PHPPgAdmin\Database\A 0 success
5215
     */
5216
    public function createDomain($domain, $type, $length, $array, $notnull, $default, $check)
5217
    {
5218
        $f_schema = $this->_schema;
5219
        $this->fieldClean($f_schema);
5220
        $this->fieldClean($domain);
5221
5222
        $sql = "CREATE DOMAIN \"{$f_schema}\".\"{$domain}\" AS ";
5223
5224
        if ($length == '') {
5225
            $sql .= $type;
5226
        } else {
5227
            switch ($type) {
5228
                // Have to account for weird placing of length for with/without
5229
                // time zone types
5230
                case 'timestamp with time zone':
5231
                case 'timestamp without time zone':
5232
                    $qual = substr($type, 9);
5233
                    $sql .= "timestamp({$length}){$qual}";
5234
                    break;
5235
                case 'time with time zone':
5236
                case 'time without time zone':
5237
                    $qual = substr($type, 4);
5238
                    $sql .= "time({$length}){$qual}";
5239
                    break;
5240
                default:
5241
                    $sql .= "{$type}({$length})";
5242
            }
5243
        }
5244
5245
        // Add array qualifier, if requested
5246
        if ($array) {
5247
            $sql .= '[]';
5248
        }
5249
5250
        if ($notnull) {
5251
            $sql .= ' NOT NULL';
5252
        }
5253
5254
        if ($default != '') {
5255
            $sql .= " DEFAULT {$default}";
5256
        }
5257
5258
        if ($this->hasDomainConstraints() && $check != '') {
5259
            $sql .= " CHECK ({$check})";
5260
        }
5261
5262
        return $this->execute($sql);
5263
    }
5264
5265
    public function hasDomainConstraints()
5266
    {
5267
        return true;
5268
    }
5269
5270
    /**
5271
     * Alters a domain.
5272
     *
5273
     * @param $domain     The domain to alter
5274
     * @param $domdefault The domain default
5275
     * @param $domnotnull True for NOT NULL, false otherwise
5276
     * @param $domowner   The domain owner
5277
     *
5278
     * @return bool|int 0 success
5279
     */
5280
    public function alterDomain($domain, $domdefault, $domnotnull, $domowner)
5281
    {
5282
        $f_schema = $this->_schema;
5283
        $this->fieldClean($f_schema);
5284
        $this->fieldClean($domain);
5285
        $this->fieldClean($domowner);
5286
5287
        $status = $this->beginTransaction();
5288
        if ($status != 0) {
5289
            $this->rollbackTransaction();
5290
5291
            return -1;
5292
        }
5293
5294
        // Default
5295
        if ($domdefault == '') {
5296
            $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP DEFAULT";
5297
        } else {
5298
            $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET DEFAULT {$domdefault}";
5299
        }
5300
5301
        $status = $this->execute($sql);
5302
        if ($status != 0) {
5303
            $this->rollbackTransaction();
5304
5305
            return -2;
5306
        }
5307
5308
        // NOT NULL
5309
        if ($domnotnull) {
5310
            $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET NOT NULL";
5311
        } else {
5312
            $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP NOT NULL";
5313
        }
5314
5315
        $status = $this->execute($sql);
5316
        if ($status != 0) {
5317
            $this->rollbackTransaction();
5318
5319
            return -3;
5320
        }
5321
5322
        // Owner
5323
        $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" OWNER TO \"{$domowner}\"";
5324
5325
        $status = $this->execute($sql);
5326
        if ($status != 0) {
5327
            $this->rollbackTransaction();
5328
5329
            return -4;
5330
        }
5331
5332
        return $this->endTransaction();
5333
    }
5334
5335
    /**
5336
     * Drops a domain.
5337
     *
5338
     * @param $domain  The name of the domain to drop
5339
     * @param $cascade True to cascade drop, false to restrict
5340
     *
5341
     * @return \PHPPgAdmin\Database\A 0 success
5342
     */
5343 View Code Duplication
    public function dropDomain($domain, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5344
    {
5345
        $f_schema = $this->_schema;
5346
        $this->fieldClean($f_schema);
5347
        $this->fieldClean($domain);
5348
5349
        $sql = "DROP DOMAIN \"{$f_schema}\".\"{$domain}\"";
5350
        if ($cascade) {
5351
            $sql .= ' CASCADE';
5352
        }
5353
5354
        return $this->execute($sql);
5355
    }
5356
5357
    /**
5358
     * Adds a check constraint to a domain.
5359
     *
5360
     * @param        $domain     The domain to which to add the check
5361
     * @param        $definition The definition of the check
5362
     * @param string $name       (optional) The name to give the check, otherwise default name is assigned
5363
     *
5364
     * @return \PHPPgAdmin\Database\A 0 success
5365
     */
5366 View Code Duplication
    public function addDomainCheckConstraint($domain, $definition, $name = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5367
    {
5368
        $f_schema = $this->_schema;
5369
        $this->fieldClean($f_schema);
5370
        $this->fieldClean($domain);
5371
        $this->fieldClean($name);
5372
5373
        $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" ADD ";
5374
        if ($name != '') {
5375
            $sql .= "CONSTRAINT \"{$name}\" ";
5376
        }
5377
5378
        $sql .= "CHECK ({$definition})";
5379
5380
        return $this->execute($sql);
5381
    }
5382
5383
    /**
5384
     * Drops a domain constraint.
5385
     *
5386
     * @param $domain     The domain from which to remove the constraint
5387
     * @param $constraint The constraint to remove
5388
     * @param $cascade    True to cascade, false otherwise
5389
     *
5390
     * @return \PHPPgAdmin\Database\A 0 success
5391
     */
5392 View Code Duplication
    public function dropDomainConstraint($domain, $constraint, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5393
    {
5394
        $f_schema = $this->_schema;
5395
        $this->fieldClean($f_schema);
5396
        $this->fieldClean($domain);
5397
        $this->fieldClean($constraint);
5398
5399
        $sql = "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP CONSTRAINT \"{$constraint}\"";
5400
        if ($cascade) {
5401
            $sql .= ' CASCADE';
5402
        }
5403
5404
        return $this->execute($sql);
5405
    }
5406
5407
    // Rule functions
5408
5409
    /**
5410
     * Returns an array containing a function's properties.
5411
     *
5412
     * @param $f The array of data for the function
5413
     *
5414
     * @return An array containing the properties
5415
     */
5416
    public function getFunctionProperties($f)
5417
    {
5418
        $temp = [];
5419
5420
        // Volatility
5421
        if ($f['provolatile'] == 'v') {
5422
            $temp[] = 'VOLATILE';
5423
        } elseif ($f['provolatile'] == 'i') {
5424
            $temp[] = 'IMMUTABLE';
5425
        } elseif ($f['provolatile'] == 's') {
5426
            $temp[] = 'STABLE';
5427
        } else {
5428
            return -1;
5429
        }
5430
5431
        // Null handling
5432
        $f['proisstrict'] = $this->phpBool($f['proisstrict']);
5433
        if ($f['proisstrict']) {
5434
            $temp[] = 'RETURNS NULL ON NULL INPUT';
5435
        } else {
5436
            $temp[] = 'CALLED ON NULL INPUT';
5437
        }
5438
5439
        // Security
5440
        $f['prosecdef'] = $this->phpBool($f['prosecdef']);
5441
        if ($f['prosecdef']) {
5442
            $temp[] = 'SECURITY DEFINER';
5443
        } else {
5444
            $temp[] = 'SECURITY INVOKER';
5445
        }
5446
5447
        return $temp;
5448
    }
5449
5450
    /**
5451
     * Updates (replaces) a function.
5452
     *
5453
     * @param $function_oid The OID of the function
5454
     * @param $funcname     The name of the function to create
5455
     * @param $newname      The new name for the function
5456
     * @param $args         The array of argument types
5457
     * @param $returns      The return type
5458
     * @param $definition   The definition for the new function
5459
     * @param $language     The language the function is written for
5460
     * @param $flags        An array of optional flags
5461
     * @param $setof        True if returns a set, false otherwise
5462
     * @param $funcown
5463
     * @param $newown
5464
     * @param $funcschema
5465
     * @param $newschema
5466
     * @param $cost
5467
     * @param $rows
5468
     * @param $comment      The comment on the function
5469
     *
5470
     * @return bool|int 0 success
5471
     */
5472
    public function setFunction(
5473
        $function_oid,
5474
        $funcname,
5475
        $newname,
5476
        $args,
5477
        $returns,
5478
        $definition,
5479
        $language,
5480
        $flags,
5481
        $setof,
5482
        $funcown,
5483
        $newown,
5484
        $funcschema,
5485
        $newschema,
5486
        $cost,
5487
        $rows,
5488
        $comment
5489
    ) {
5490
        // Begin a transaction
5491
        $status = $this->beginTransaction();
5492
        if ($status != 0) {
5493
            $this->rollbackTransaction();
5494
5495
            return -1;
5496
        }
5497
5498
        // Replace the existing function
5499
        $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, true);
5500
        if ($status != 0) {
5501
            $this->rollbackTransaction();
5502
5503
            return $status;
5504
        }
5505
5506
        $f_schema = $this->_schema;
5507
        $this->fieldClean($f_schema);
5508
5509
        // Rename the function, if necessary
5510
        $this->fieldClean($newname);
5511
        /* $funcname is escaped in createFunction */
5512
        if ($funcname != $newname) {
5513
            $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
5514
            $status = $this->execute($sql);
5515
            if ($status != 0) {
5516
                $this->rollbackTransaction();
5517
5518
                return -5;
5519
            }
5520
5521
            $funcname = $newname;
5522
        }
5523
5524
        // Alter the owner, if necessary
5525
        if ($this->hasFunctionAlterOwner()) {
5526
            $this->fieldClean($newown);
5527
            if ($funcown != $newown) {
5528
                $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
5529
                $status = $this->execute($sql);
5530
                if ($status != 0) {
5531
                    $this->rollbackTransaction();
5532
5533
                    return -6;
5534
                }
5535
            }
5536
        }
5537
5538
        // Alter the schema, if necessary
5539
        if ($this->hasFunctionAlterSchema()) {
5540
            $this->fieldClean($newschema);
5541
            /* $funcschema is escaped in createFunction */
5542
            if ($funcschema != $newschema) {
5543
                $sql = "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
5544
                $status = $this->execute($sql);
5545
                if ($status != 0) {
5546
                    $this->rollbackTransaction();
5547
5548
                    return -7;
5549
                }
5550
            }
5551
        }
5552
5553
        return $this->endTransaction();
5554
    }
5555
5556
    /**
5557
     * Creates a new function.
5558
     *
5559
     * @param      $funcname   The name of the function to create
5560
     * @param      $args       A comma separated string of types
5561
     * @param      $returns    The return type
5562
     * @param      $definition The definition for the new function
5563
     * @param      $language   The language the function is written for
5564
     * @param      $flags      An array of optional flags
5565
     * @param      $setof      True if it returns a set, false otherwise
5566
     * @param      $cost       cost the planner should use in the function execution step
5567
     * @param      $rows       number of rows planner should estimate will be returned
5568
     * @param      $comment    Comment for the function
5569
     * @param bool $replace    (optional) True if OR REPLACE, false for normal
5570
     *
5571
     * @return bool|int 0 success
5572
     */
5573
    public function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $comment, $replace = false)
5574
    {
5575
5576
        // Begin a transaction
5577
        $status = $this->beginTransaction();
5578
        if ($status != 0) {
5579
            $this->rollbackTransaction();
5580
5581
            return -1;
5582
        }
5583
5584
        $this->fieldClean($funcname);
5585
        $this->clean($args);
5586
        $this->fieldClean($language);
5587
        $this->arrayClean($flags);
5588
        $this->clean($cost);
5589
        $this->clean($rows);
5590
        $f_schema = $this->_schema;
5591
        $this->fieldClean($f_schema);
5592
5593
        $sql = 'CREATE';
5594
        if ($replace) {
5595
            $sql .= ' OR REPLACE';
5596
        }
5597
5598
        $sql .= " FUNCTION \"{$f_schema}\".\"{$funcname}\" (";
5599
5600
        if ($args != '') {
5601
            $sql .= $args;
5602
        }
5603
5604
        // For some reason, the returns field cannot have quotes...
5605
        $sql .= ') RETURNS ';
5606
        if ($setof) {
5607
            $sql .= 'SETOF ';
5608
        }
5609
5610
        $sql .= "{$returns} AS ";
5611
5612 View Code Duplication
        if (is_array($definition)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5613
            $this->arrayClean($definition);
5614
            $sql .= "'".$definition[0]."'";
5615
            if ($definition[1]) {
5616
                $sql .= ",'".$definition[1]."'";
5617
            }
5618
        } else {
5619
            $this->clean($definition);
5620
            $sql .= "'".$definition."'";
5621
        }
5622
5623
        $sql .= " LANGUAGE \"{$language}\"";
5624
5625
        // Add costs
5626
        if (!empty($cost)) {
5627
            $sql .= " COST {$cost}";
5628
        }
5629
5630
        if ($rows != 0) {
5631
            $sql .= " ROWS {$rows}";
5632
        }
5633
5634
        // Add flags
5635
        foreach ($flags as $v) {
5636
            // Skip default flags
5637
            if ($v == '') {
5638
                continue;
5639
            }
5640
5641
            $sql .= "\n{$v}";
5642
        }
5643
5644
        $status = $this->execute($sql);
5645
        if ($status != 0) {
5646
            $this->rollbackTransaction();
5647
5648
            return -3;
5649
        }
5650
5651
        /* set the comment */
5652
        $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
5653
        if ($status != 0) {
5654
            $this->rollbackTransaction();
5655
5656
            return -4;
5657
        }
5658
5659
        return $this->endTransaction();
5660
    }
5661
5662
    public function hasFunctionAlterOwner()
5663
    {
5664
        return true;
5665
    }
5666
5667
    // Trigger functions
5668
5669
    public function hasFunctionAlterSchema()
5670
    {
5671
        return true;
5672
    }
5673
5674
    /**
5675
     * Drops a function.
5676
     *
5677
     * @param $function_oid The OID of the function to drop
5678
     * @param $cascade      True to cascade drop, false to restrict
5679
     *
5680
     * @return \PHPPgAdmin\Database\A 0 success
5681
     */
5682
    public function dropFunction($function_oid, $cascade)
5683
    {
5684
        // Function comes in with $object as function OID
5685
        $fn = $this->getFunction($function_oid);
5686
        $f_schema = $this->_schema;
5687
        $this->fieldClean($f_schema);
5688
        $this->fieldClean($fn->fields['proname']);
5689
5690
        $sql = "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
5691
        if ($cascade) {
5692
            $sql .= ' CASCADE';
5693
        }
5694
5695
        return $this->execute($sql);
5696
    }
5697
5698
    /**
5699
     * Returns all details for a particular function.
5700
     *
5701
     * @param $function_oid
5702
     *
5703
     * @return \PHPPgAdmin\Database\Function info
5704
     *
5705
     * @internal param \PHPPgAdmin\Database\The $func name of the function to retrieve
5706
     */
5707
    public function getFunction($function_oid)
5708
    {
5709
        $this->clean($function_oid);
5710
5711
        $sql = "
5712
			SELECT
5713
				pc.oid AS prooid, proname,
5714
				pg_catalog.pg_get_userbyid(proowner) AS proowner,
5715
				nspname as proschema, lanname as prolanguage, procost, prorows,
5716
				pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
5717
				probin, proretset, proisstrict, provolatile, prosecdef,
5718
				pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
5719
				proargnames AS proargnames,
5720
				pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
5721
				proconfig,
5722
				(select array_agg( (select typname from pg_type pt
5723
					where pt.oid = p.oid) ) from unnest(proallargtypes) p)
5724
				AS proallarguments,
5725
				proargmodes
5726
			FROM
5727
				pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
5728
				pg_catalog.pg_namespace pn
5729
			WHERE
5730
				pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid
5731
				AND pc.pronamespace = pn.oid
5732
			";
5733
5734
        return $this->selectSet($sql);
5735
    }
5736
5737
    /**
5738
     * Returns all details for a particular type.
5739
     *
5740
     * @param $typname The name of the view to retrieve
5741
     *
5742
     * @return Type info
5743
     */
5744
    public function getType($typname)
5745
    {
5746
        $this->clean($typname);
5747
5748
        $sql = "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign
5749
			FROM pg_type WHERE typname='{$typname}'";
5750
5751
        return $this->selectSet($sql);
5752
    }
5753
5754
    /**
5755
     * Returns a list of all types in the database.
5756
     *
5757
     * @param bool|\PHPPgAdmin\Database\If $all        If true, will find all available types, if false just those in search path
5758
     * @param bool|\PHPPgAdmin\Database\If $tabletypes If true, will include table types
5759
     * @param bool|\PHPPgAdmin\Database\If $domains    If true, will include domains
5760
     *
5761
     * @return \PHPPgAdmin\Database\A recordet
5762
     */
5763
    public function getTypes($all = false, $tabletypes = false, $domains = false)
5764
    {
5765
        if ($all) {
5766
            $where = '1 = 1';
5767
        } else {
5768
            $c_schema = $this->_schema;
5769
            $this->clean($c_schema);
5770
            $where = "n.nspname = '{$c_schema}'";
5771
        }
5772
        // Never show system table types
5773
        $where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg@_%' ESCAPE '@')";
5774
5775
        // Create type filter
5776
        $tqry = "'c'";
5777
        if ($tabletypes) {
5778
            $tqry .= ", 'r', 'v'";
5779
        }
5780
5781
        // Create domain filter
5782
        if (!$domains) {
5783
            $where .= " AND t.typtype != 'd'";
5784
        }
5785
5786
        $sql = "SELECT
5787
				t.typname AS basename,
5788
				pg_catalog.format_type(t.oid, NULL) AS typname,
5789
				pu.usename AS typowner,
5790
				t.typtype,
5791
				pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment
5792
			FROM (pg_catalog.pg_type t
5793
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace)
5794
				LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid
5795
			WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2}))
5796
			AND t.typname !~ '^_'
5797
			AND {$where}
5798
			ORDER BY typname
5799
		";
5800
5801
        return $this->selectSet($sql);
5802
    }
5803
5804
    /**
5805
     * Creates a new type.
5806
     *
5807
     * @param $typname
5808
     * @param $typin
5809
     * @param $typout
5810
     * @param $typlen
5811
     * @param $typdef
5812
     * @param $typelem
5813
     * @param $typdelim
5814
     * @param $typbyval
5815
     * @param $typalign
5816
     * @param $typstorage
5817
     *
5818
     * @return \PHPPgAdmin\Database\A 0 success
5819
     *
5820
     * @internal param $ ...
5821
     */
5822
    public function createType(
5823
        $typname,
5824
        $typin,
5825
        $typout,
5826
        $typlen,
5827
        $typdef,
5828
        $typelem,
5829
        $typdelim,
5830
        $typbyval,
5831
        $typalign,
5832
        $typstorage
5833
    ) {
5834
        $f_schema = $this->_schema;
5835
        $this->fieldClean($f_schema);
5836
        $this->fieldClean($typname);
5837
        $this->fieldClean($typin);
5838
        $this->fieldClean($typout);
5839
5840
        $sql = "
5841
			CREATE TYPE \"{$f_schema}\".\"{$typname}\" (
5842
				INPUT = \"{$typin}\",
5843
				OUTPUT = \"{$typout}\",
5844
				INTERNALLENGTH = {$typlen}";
5845
        if ($typdef != '') {
5846
            $sql .= ", DEFAULT = {$typdef}";
5847
        }
5848
5849
        if ($typelem != '') {
5850
            $sql .= ", ELEMENT = {$typelem}";
5851
        }
5852
5853
        if ($typdelim != '') {
5854
            $sql .= ", DELIMITER = {$typdelim}";
5855
        }
5856
5857
        if ($typbyval) {
5858
            $sql .= ', PASSEDBYVALUE, ';
5859
        }
5860
5861
        if ($typalign != '') {
5862
            $sql .= ", ALIGNMENT = {$typalign}";
5863
        }
5864
5865
        if ($typstorage != '') {
5866
            $sql .= ", STORAGE = {$typstorage}";
5867
        }
5868
5869
        $sql .= ')';
5870
5871
        return $this->execute($sql);
5872
    }
5873
5874
    /**
5875
     * Drops a type.
5876
     *
5877
     * @param $typname The name of the type to drop
5878
     * @param $cascade True to cascade drop, false to restrict
5879
     *
5880
     * @return \PHPPgAdmin\Database\A 0 success
5881
     */
5882 View Code Duplication
    public function dropType($typname, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5883
    {
5884
        $f_schema = $this->_schema;
5885
        $this->fieldClean($f_schema);
5886
        $this->fieldClean($typname);
5887
5888
        $sql = "DROP TYPE \"{$f_schema}\".\"{$typname}\"";
5889
        if ($cascade) {
5890
            $sql .= ' CASCADE';
5891
        }
5892
5893
        return $this->execute($sql);
5894
    }
5895
5896
    /**
5897
     * Creates a new enum type in the database.
5898
     *
5899
     * @param $name       The name of the type
5900
     * @param $values     An array of values
5901
     * @param $typcomment Type comment
5902
     *
5903
     * @return bool|int 0 success
5904
     */
5905
    public function createEnumType($name, $values, $typcomment)
5906
    {
5907
        $f_schema = $this->_schema;
5908
        $this->fieldClean($f_schema);
5909
        $this->fieldClean($name);
5910
5911
        if (empty($values)) {
5912
            return -2;
5913
        }
5914
5915
        $status = $this->beginTransaction();
5916
        if ($status != 0) {
5917
            return -1;
5918
        }
5919
5920
        $values = array_unique($values);
5921
5922
        $nbval = count($values);
5923
5924
        for ($i = 0; $i < $nbval; $i++) {
5925
            $this->clean($values[$i]);
5926
        }
5927
5928
        $sql = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS ENUM ('";
5929
        $sql .= implode("','", $values);
5930
        $sql .= "')";
5931
5932
        $status = $this->execute($sql);
5933
        if ($status) {
5934
            $this->rollbackTransaction();
5935
5936
            return -1;
5937
        }
5938
5939 View Code Duplication
        if ($typcomment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
5940
            $status = $this->setComment('TYPE', $name, '', $typcomment, true);
5941
            if ($status) {
5942
                $this->rollbackTransaction();
5943
5944
                return -1;
5945
            }
5946
        }
5947
5948
        return $this->endTransaction();
5949
    }
5950
5951
    /**
5952
     * Get defined values for a given enum.
5953
     *
5954
     * @param $name
5955
     *
5956
     * @return \PHPPgAdmin\Database\A recordset
5957
     */
5958
    public function getEnumValues($name)
5959
    {
5960
        $this->clean($name);
5961
5962
        $sql = "SELECT enumlabel AS enumval
5963
		FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON (t.oid=e.enumtypid)
5964
		WHERE t.typname = '{$name}' ORDER BY e.oid";
5965
5966
        return $this->selectSet($sql);
5967
    }
5968
5969
    // Operator functions
5970
5971
    /**
5972
     * Creates a new composite type in the database.
5973
     *
5974
     * @param $name       The name of the type
5975
     * @param $fields     The number of fields
5976
     * @param $field      An array of field names
5977
     * @param $type       An array of field types
5978
     * @param $array      An array of '' or '[]' for each type if it's an array or not
5979
     * @param $length     An array of field lengths
5980
     * @param $colcomment An array of comments
5981
     * @param $typcomment Type comment
5982
     *
5983
     * @return bool|int 0 success
5984
     */
5985
    public function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment)
5986
    {
5987
        $f_schema = $this->_schema;
5988
        $this->fieldClean($f_schema);
5989
        $this->fieldClean($name);
5990
5991
        $status = $this->beginTransaction();
5992
        if ($status != 0) {
5993
            return -1;
5994
        }
5995
5996
        $found = false;
5997
        $first = true;
5998
        $comment_sql = ''; // Accumulate comments for the columns
5999
        $sql = "CREATE TYPE \"{$f_schema}\".\"{$name}\" AS (";
6000
        for ($i = 0; $i < $fields; $i++) {
6001
            $this->fieldClean($field[$i]);
6002
            $this->clean($type[$i]);
6003
            $this->clean($length[$i]);
6004
            $this->clean($colcomment[$i]);
6005
6006
            // Skip blank columns - for user convenience
6007
            if ($field[$i] == '' || $type[$i] == '') {
6008
                continue;
6009
            }
6010
6011
            // If not the first column, add a comma
6012
            if (!$first) {
6013
                $sql .= ', ';
6014
            } else {
6015
                $first = false;
6016
            }
6017
6018 View Code Duplication
            switch ($type[$i]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6019
                // Have to account for weird placing of length for with/without
6020
                // time zone types
6021
                case 'timestamp with time zone':
6022
                case 'timestamp without time zone':
6023
                    $qual = substr($type[$i], 9);
6024
                    $sql .= "\"{$field[$i]}\" timestamp";
6025
                    if ($length[$i] != '') {
6026
                        $sql .= "({$length[$i]})";
6027
                    }
6028
6029
                    $sql .= $qual;
6030
                    break;
6031
                case 'time with time zone':
6032
                case 'time without time zone':
6033
                    $qual = substr($type[$i], 4);
6034
                    $sql .= "\"{$field[$i]}\" time";
6035
                    if ($length[$i] != '') {
6036
                        $sql .= "({$length[$i]})";
6037
                    }
6038
6039
                    $sql .= $qual;
6040
                    break;
6041
                default:
6042
                    $sql .= "\"{$field[$i]}\" {$type[$i]}";
6043
                    if ($length[$i] != '') {
6044
                        $sql .= "({$length[$i]})";
6045
                    }
6046
            }
6047
            // Add array qualifier if necessary
6048
            if ($array[$i] == '[]') {
6049
                $sql .= '[]';
6050
            }
6051
6052 View Code Duplication
            if ($colcomment[$i] != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6053
                $comment_sql .= "COMMENT ON COLUMN \"{$f_schema}\".\"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
6054
            }
6055
6056
            $found = true;
6057
        }
6058
6059
        if (!$found) {
6060
            return -1;
6061
        }
6062
6063
        $sql .= ')';
6064
6065
        $status = $this->execute($sql);
6066
        if ($status) {
6067
            $this->rollbackTransaction();
6068
6069
            return -1;
6070
        }
6071
6072 View Code Duplication
        if ($typcomment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6073
            $status = $this->setComment('TYPE', $name, '', $typcomment, true);
6074
            if ($status) {
6075
                $this->rollbackTransaction();
6076
6077
                return -1;
6078
            }
6079
        }
6080
6081 View Code Duplication
        if ($comment_sql != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6082
            $status = $this->execute($comment_sql);
6083
            if ($status) {
6084
                $this->rollbackTransaction();
6085
6086
                return -1;
6087
            }
6088
        }
6089
6090
        return $this->endTransaction();
6091
    }
6092
6093
    /**
6094
     * Returns a list of all casts in the database.
6095
     *
6096
     * @return All casts
6097
     */
6098 View Code Duplication
    public function getCasts()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6099
    {
6100
        $conf = $this->conf;
6101
6102
        if ($conf['show_system']) {
6103
            $where = '';
6104
        } else {
6105
            $where = '
6106
				AND n1.nspname NOT LIKE $$pg\_%$$
6107
				AND n2.nspname NOT LIKE $$pg\_%$$
6108
				AND n3.nspname NOT LIKE $$pg\_%$$
6109
			';
6110
        }
6111
6112
        $sql = "
6113
			SELECT
6114
				c.castsource::pg_catalog.regtype AS castsource,
6115
				c.casttarget::pg_catalog.regtype AS casttarget,
6116
				CASE WHEN c.castfunc=0 THEN NULL
6117
				ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc,
6118
				c.castcontext,
6119
				obj_description(c.oid, 'pg_cast') as castcomment
6120
			FROM
6121
				(pg_catalog.pg_cast c LEFT JOIN pg_catalog.pg_proc p ON c.castfunc=p.oid JOIN pg_catalog.pg_namespace n3 ON p.pronamespace=n3.oid),
6122
				pg_catalog.pg_type t1,
6123
				pg_catalog.pg_type t2,
6124
				pg_catalog.pg_namespace n1,
6125
				pg_catalog.pg_namespace n2
6126
			WHERE
6127
				c.castsource=t1.oid
6128
				AND c.casttarget=t2.oid
6129
				AND t1.typnamespace=n1.oid
6130
				AND t2.typnamespace=n2.oid
6131
				{$where}
6132
			ORDER BY 1, 2
6133
		";
6134
6135
        return $this->selectSet($sql);
6136
    }
6137
6138
    /**
6139
     * Returns a list of all conversions in the database.
6140
     *
6141
     * @return All conversions
6142
     */
6143
    public function getConversions()
6144
    {
6145
        $c_schema = $this->_schema;
6146
        $this->clean($c_schema);
6147
        $sql = "
6148
			SELECT
6149
			       c.conname,
6150
			       pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding,
6151
			       pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding,
6152
			       c.condefault,
6153
			       pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment
6154
			FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n
6155
			WHERE n.oid = c.connamespace
6156
			      AND n.nspname='{$c_schema}'
6157
			ORDER BY 1;
6158
		";
6159
6160
        return $this->selectSet($sql);
6161
    }
6162
6163
    // Operator Class functions
6164
6165
    /**
6166
     * Edits a rule on a table OR view.
6167
     *
6168
     * @param $name    The name of the new rule
6169
     * @param $event   SELECT, INSERT, UPDATE or DELETE
6170
     * @param $table   Table on which to create the rule
6171
     * @param $where   When to execute the rule, '' indicates always
6172
     * @param $instead True if an INSTEAD rule, false otherwise
6173
     * @param $type    NOTHING for a do nothing rule, SOMETHING to use given action
6174
     * @param $action  The action to take
6175
     *
6176
     * @return \PHPPgAdmin\Database\A 0 success
6177
     */
6178
    public function setRule($name, $event, $table, $where, $instead, $type, $action)
6179
    {
6180
        return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true);
6181
    }
6182
6183
    // FTS functions
6184
6185
    /**
6186
     * Creates a rule.
6187
     *
6188
     * @param      $name    The name of the new rule
6189
     * @param      $event   SELECT, INSERT, UPDATE or DELETE
6190
     * @param      $table   Table on which to create the rule
6191
     * @param      $where   When to execute the rule, '' indicates always
6192
     * @param      $instead True if an INSTEAD rule, false otherwise
6193
     * @param      $type    NOTHING for a do nothing rule, SOMETHING to use given action
6194
     * @param      $action  The action to take
6195
     * @param bool $replace (optional) True to replace existing rule, false otherwise
6196
     *
6197
     * @return \PHPPgAdmin\Database\A 0 success
6198
     */
6199
    public function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false)
6200
    {
6201
        $f_schema = $this->_schema;
6202
        $this->fieldClean($f_schema);
6203
        $this->fieldClean($name);
6204
        $this->fieldClean($table);
6205
        if (!in_array($event, $this->rule_events)) {
6206
            return -1;
6207
        }
6208
6209
        $sql = 'CREATE';
6210
        if ($replace) {
6211
            $sql .= ' OR REPLACE';
6212
        }
6213
6214
        $sql .= " RULE \"{$name}\" AS ON {$event} TO \"{$f_schema}\".\"{$table}\"";
6215
        // Can't escape WHERE clause
6216
        if ($where != '') {
6217
            $sql .= " WHERE {$where}";
6218
        }
6219
6220
        $sql .= ' DO';
6221
        if ($instead) {
6222
            $sql .= ' INSTEAD';
6223
        }
6224
6225
        if ($type == 'NOTHING') {
6226
            $sql .= ' NOTHING';
6227
        } else {
6228
            $sql .= " ({$action})";
6229
        }
6230
6231
        return $this->execute($sql);
6232
    }
6233
6234
    /**
6235
     * Removes a rule from a table OR view.
6236
     *
6237
     * @param $rule     The rule to drop
6238
     * @param $relation The relation from which to drop
6239
     * @param $cascade  True to cascade drop, false to restrict
6240
     *
6241
     * @return \PHPPgAdmin\Database\A 0 success
6242
     */
6243 View Code Duplication
    public function dropRule($rule, $relation, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6244
    {
6245
        $f_schema = $this->_schema;
6246
        $this->fieldClean($f_schema);
6247
        $this->fieldClean($rule);
6248
        $this->fieldClean($relation);
6249
6250
        $sql = "DROP RULE \"{$rule}\" ON \"{$f_schema}\".\"{$relation}\"";
6251
        if ($cascade) {
6252
            $sql .= ' CASCADE';
6253
        }
6254
6255
        return $this->execute($sql);
6256
    }
6257
6258
    /**
6259
     * Grabs a single trigger.
6260
     *
6261
     * @param $table   The name of a table whose triggers to retrieve
6262
     * @param $trigger The name of the trigger to retrieve
6263
     *
6264
     * @return A recordset
6265
     */
6266 View Code Duplication
    public function getTrigger($table, $trigger)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6267
    {
6268
        $c_schema = $this->_schema;
6269
        $this->clean($c_schema);
6270
        $this->clean($table);
6271
        $this->clean($trigger);
6272
6273
        $sql = "
6274
			SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c
6275
			WHERE t.tgrelid=c.oid AND c.relname='{$table}' AND t.tgname='{$trigger}'
6276
				AND c.relnamespace=(
6277
					SELECT oid FROM pg_catalog.pg_namespace
6278
					WHERE nspname='{$c_schema}')";
6279
6280
        return $this->selectSet($sql);
6281
    }
6282
6283
    /**
6284
     * A helper function for getTriggers that translates
6285
     * an array of attribute numbers to an array of field names.
6286
     * Note: Only needed for pre-7.4 servers, this function is deprecated.
6287
     *
6288
     * @param $trigger An array containing fields from the trigger table
6289
     *
6290
     * @return The trigger definition string
6291
     */
6292
    public function getTriggerDef($trigger)
6293
    {
6294
        $this->fieldArrayClean($trigger);
6295
        // Constants to figure out tgtype
6296
        if (!defined('TRIGGER_TYPE_ROW')) {
6297
            define('TRIGGER_TYPE_ROW', 1 << 0);
6298
        }
6299
6300
        if (!defined('TRIGGER_TYPE_BEFORE')) {
6301
            define('TRIGGER_TYPE_BEFORE', 1 << 1);
6302
        }
6303
6304
        if (!defined('TRIGGER_TYPE_INSERT')) {
6305
            define('TRIGGER_TYPE_INSERT', 1 << 2);
6306
        }
6307
6308
        if (!defined('TRIGGER_TYPE_DELETE')) {
6309
            define('TRIGGER_TYPE_DELETE', 1 << 3);
6310
        }
6311
6312
        if (!defined('TRIGGER_TYPE_UPDATE')) {
6313
            define('TRIGGER_TYPE_UPDATE', 1 << 4);
6314
        }
6315
6316
        $trigger['tgisconstraint'] = $this->phpBool($trigger['tgisconstraint']);
6317
        $trigger['tgdeferrable'] = $this->phpBool($trigger['tgdeferrable']);
6318
        $trigger['tginitdeferred'] = $this->phpBool($trigger['tginitdeferred']);
6319
6320
        // Constraint trigger or normal trigger
6321
        if ($trigger['tgisconstraint']) {
6322
            $tgdef = 'CREATE CONSTRAINT TRIGGER ';
6323
        } else {
6324
            $tgdef = 'CREATE TRIGGER ';
6325
        }
6326
6327
        $tgdef .= "\"{$trigger['tgname']}\" ";
6328
6329
        // Trigger type
6330
        $findx = 0;
6331
        if (($trigger['tgtype'] & TRIGGER_TYPE_BEFORE) == TRIGGER_TYPE_BEFORE) {
6332
            $tgdef .= 'BEFORE';
6333
        } else {
6334
            $tgdef .= 'AFTER';
6335
        }
6336
6337
        if (($trigger['tgtype'] & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT) {
6338
            $tgdef .= ' INSERT';
6339
            $findx++;
6340
        }
6341 View Code Duplication
        if (($trigger['tgtype'] & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6342
            if ($findx > 0) {
6343
                $tgdef .= ' OR DELETE';
6344
            } else {
6345
                $tgdef .= ' DELETE';
6346
                $findx++;
6347
            }
6348
        }
6349 View Code Duplication
        if (($trigger['tgtype'] & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6350
            if ($findx > 0) {
6351
                $tgdef .= ' OR UPDATE';
6352
            } else {
6353
                $tgdef .= ' UPDATE';
6354
            }
6355
        }
6356
6357
        $f_schema = $this->_schema;
6358
        $this->fieldClean($f_schema);
6359
        // Table name
6360
        $tgdef .= " ON \"{$f_schema}\".\"{$trigger['relname']}\" ";
6361
6362
        // Deferrability
6363
        if ($trigger['tgisconstraint']) {
6364
            if ($trigger['tgconstrrelid'] != 0) {
6365
                // Assume constrelname is not null
6366
                $tgdef .= " FROM \"{$trigger['tgconstrrelname']}\" ";
6367
            }
6368
            if (!$trigger['tgdeferrable']) {
6369
                $tgdef .= 'NOT ';
6370
            }
6371
6372
            $tgdef .= 'DEFERRABLE INITIALLY ';
6373
            if ($trigger['tginitdeferred']) {
6374
                $tgdef .= 'DEFERRED ';
6375
            } else {
6376
                $tgdef .= 'IMMEDIATE ';
6377
            }
6378
        }
6379
6380
        // Row or statement
6381
        if ($trigger['tgtype'] & TRIGGER_TYPE_ROW == TRIGGER_TYPE_ROW) {
6382
            $tgdef .= 'FOR EACH ROW ';
6383
        } else {
6384
            $tgdef .= 'FOR EACH STATEMENT ';
6385
        }
6386
6387
        // Execute procedure
6388
        $tgdef .= "EXECUTE PROCEDURE \"{$trigger['tgfname']}\"(";
6389
6390
        // Parameters
6391
        // Escape null characters
6392
        $v = addcslashes($trigger['tgargs'], "\0");
6393
        // Split on escaped null characters
6394
        $params = explode('\\000', $v);
6395
        for ($findx = 0; $findx < $trigger['tgnargs']; $findx++) {
6396
            $param = "'".str_replace('\'', '\\\'', $params[$findx])."'";
6397
            $tgdef .= $param;
6398
            if ($findx < ($trigger['tgnargs'] - 1)) {
6399
                $tgdef .= ', ';
6400
            }
6401
        }
6402
6403
        // Finish it off
6404
        $tgdef .= ')';
6405
6406
        return $tgdef;
6407
    }
6408
6409
    /**
6410
     * Returns a list of all functions that can be used in triggers.
6411
     */
6412
    public function getTriggerFunctions()
6413
    {
6414
        return $this->getFunctions(true, 'trigger');
6415
    }
6416
6417
    /**
6418
     * Returns a list of all functions in the database.
6419
     *
6420
     * @param bool|\PHPPgAdmin\Database\If $all  If true, will find all available functions, if false just those in search path
6421
     * @param                              $type If not null, will find all functions with return value = type
6422
     *
6423
     * @return \PHPPgAdmin\Database\All functions
6424
     */
6425
    public function getFunctions($all = false, $type = null)
6426
    {
6427
        if ($all) {
6428
            $where = 'pg_catalog.pg_function_is_visible(p.oid)';
6429
            $distinct = 'DISTINCT ON (p.proname)';
6430
6431
            if ($type) {
6432
                $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
6433
            }
6434
        } else {
6435
            $c_schema = $this->_schema;
6436
            $this->clean($c_schema);
6437
            $where = "n.nspname = '{$c_schema}'";
6438
            $distinct = '';
6439
        }
6440
6441
        $sql = "
6442
			SELECT
6443
				{$distinct}
6444
				p.oid AS prooid,
6445
				p.proname,
6446
				p.proretset,
6447
				pg_catalog.format_type(p.prorettype, NULL) AS proresult,
6448
				pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
6449
				pl.lanname AS prolanguage,
6450
				pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment,
6451
				p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
6452
				CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
6453
				u.usename AS proowner
6454
			FROM pg_catalog.pg_proc p
6455
				INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
6456
				INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
6457
				LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
6458
			WHERE NOT p.proisagg
6459
				AND {$where}
6460
			ORDER BY p.proname, proresult
6461
			";
6462
6463
        return $this->selectSet($sql);
6464
    }
6465
6466
    /**
6467
     * Creates a trigger.
6468
     *
6469
     * @param $tgname  The name of the trigger to create
6470
     * @param $table   The name of the table
6471
     * @param $tgproc  The function to execute
6472
     * @param $tgtime  BEFORE or AFTER
6473
     * @param $tgevent Event
6474
     * @param $tgfrequency
6475
     * @param $tgargs  The function arguments
6476
     *
6477
     * @return \PHPPgAdmin\Database\A 0 success
6478
     */
6479
    public function createTrigger($tgname, $table, $tgproc, $tgtime, $tgevent, $tgfrequency, $tgargs)
6480
    {
6481
        $f_schema = $this->_schema;
6482
        $this->fieldClean($f_schema);
6483
        $this->fieldClean($tgname);
6484
        $this->fieldClean($table);
6485
        $this->fieldClean($tgproc);
6486
6487
        /* No Statement Level Triggers in PostgreSQL (by now) */
6488
        $sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime}
6489
				{$tgevent} ON \"{$f_schema}\".\"{$table}\"
6490
				FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})";
6491
6492
        return $this->execute($sql);
6493
    }
6494
6495
    /**
6496
     * Alters a trigger.
6497
     *
6498
     * @param $table   The name of the table containing the trigger
6499
     * @param $trigger The name of the trigger to alter
6500
     * @param $name    The new name for the trigger
6501
     *
6502
     * @return \PHPPgAdmin\Database\A 0 success
6503
     */
6504
    public function alterTrigger($table, $trigger, $name)
6505
    {
6506
        $f_schema = $this->_schema;
6507
        $this->fieldClean($f_schema);
6508
        $this->fieldClean($table);
6509
        $this->fieldClean($trigger);
6510
        $this->fieldClean($name);
6511
6512
        $sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$f_schema}\".\"{$table}\" RENAME TO \"{$name}\"";
6513
6514
        return $this->execute($sql);
6515
    }
6516
6517
    /**
6518
     * Drops a trigger.
6519
     *
6520
     * @param $tgname  The name of the trigger to drop
6521
     * @param $table   The table from which to drop the trigger
6522
     * @param $cascade True to cascade drop, false to restrict
6523
     *
6524
     * @return \PHPPgAdmin\Database\A 0 success
6525
     */
6526 View Code Duplication
    public function dropTrigger($tgname, $table, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6527
    {
6528
        $f_schema = $this->_schema;
6529
        $this->fieldClean($f_schema);
6530
        $this->fieldClean($tgname);
6531
        $this->fieldClean($table);
6532
6533
        $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$f_schema}\".\"{$table}\"";
6534
        if ($cascade) {
6535
            $sql .= ' CASCADE';
6536
        }
6537
6538
        return $this->execute($sql);
6539
    }
6540
6541
    /**
6542
     * Enables a trigger.
6543
     *
6544
     * @param $tgname The name of the trigger to enable
6545
     * @param $table  The table in which to enable the trigger
6546
     *
6547
     * @return \PHPPgAdmin\Database\A 0 success
6548
     */
6549 View Code Duplication
    public function enableTrigger($tgname, $table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6550
    {
6551
        $f_schema = $this->_schema;
6552
        $this->fieldClean($f_schema);
6553
        $this->fieldClean($tgname);
6554
        $this->fieldClean($table);
6555
6556
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" ENABLE TRIGGER \"{$tgname}\"";
6557
6558
        return $this->execute($sql);
6559
    }
6560
6561
    /**
6562
     * Disables a trigger.
6563
     *
6564
     * @param $tgname The name of the trigger to disable
6565
     * @param $table  The table in which to disable the trigger
6566
     *
6567
     * @return \PHPPgAdmin\Database\A 0 success
6568
     */
6569 View Code Duplication
    public function disableTrigger($tgname, $table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6570
    {
6571
        $f_schema = $this->_schema;
6572
        $this->fieldClean($f_schema);
6573
        $this->fieldClean($tgname);
6574
        $this->fieldClean($table);
6575
6576
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" DISABLE TRIGGER \"{$tgname}\"";
6577
6578
        return $this->execute($sql);
6579
    }
6580
6581
    /**
6582
     * Returns a list of all operators in the database.
6583
     *
6584
     * @return All operators
6585
     */
6586
    public function getOperators()
6587
    {
6588
        $c_schema = $this->_schema;
6589
        $this->clean($c_schema);
6590
        // We stick with the subselects here, as you cannot ORDER BY a regtype
6591
        $sql = "
6592
			SELECT
6593
            	po.oid,	po.oprname,
6594
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
6595
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
6596
				po.oprresult::pg_catalog.regtype AS resultname,
6597
		        pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment
6598
			FROM
6599
				pg_catalog.pg_operator po
6600
			WHERE
6601
				po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$c_schema}')
6602
			ORDER BY
6603
				po.oprname, oprleftname, oprrightname
6604
		";
6605
6606
        return $this->selectSet($sql);
6607
    }
6608
6609
    /**
6610
     * Drops an operator.
6611
     *
6612
     * @param $operator_oid The OID of the operator to drop
6613
     * @param $cascade      True to cascade drop, false to restrict
6614
     *
6615
     * @return \PHPPgAdmin\Database\A 0 success
6616
     */
6617
    public function dropOperator($operator_oid, $cascade)
6618
    {
6619
        // Function comes in with $object as operator OID
6620
        $opr = $this->getOperator($operator_oid);
6621
        $f_schema = $this->_schema;
6622
        $this->fieldClean($f_schema);
6623
        $this->fieldClean($opr->fields['oprname']);
6624
6625
        $sql = "DROP OPERATOR \"{$f_schema}\".{$opr->fields['oprname']} (";
6626
        // Quoting or formatting here???
6627
        if ($opr->fields['oprleftname'] !== null) {
6628
            $sql .= $opr->fields['oprleftname'].', ';
6629
        } else {
6630
            $sql .= 'NONE, ';
6631
        }
6632
6633
        if ($opr->fields['oprrightname'] !== null) {
6634
            $sql .= $opr->fields['oprrightname'].')';
6635
        } else {
6636
            $sql .= 'NONE)';
6637
        }
6638
6639
        if ($cascade) {
6640
            $sql .= ' CASCADE';
6641
        }
6642
6643
        return $this->execute($sql);
6644
    }
6645
6646
    /**
6647
     * Returns all details for a particular operator.
6648
     *
6649
     * @param $operator_oid The oid of the operator
6650
     *
6651
     * @return Function info
6652
     */
6653
    public function getOperator($operator_oid)
6654
    {
6655
        $this->clean($operator_oid);
6656
6657
        $sql = "
6658
			SELECT
6659
            	po.oid, po.oprname,
6660
				oprleft::pg_catalog.regtype AS oprleftname,
6661
				oprright::pg_catalog.regtype AS oprrightname,
6662
				oprresult::pg_catalog.regtype AS resultname,
6663
				po.oprcanhash,
6664
				oprcanmerge,
6665
				oprcom::pg_catalog.regoperator AS oprcom,
6666
				oprnegate::pg_catalog.regoperator AS oprnegate,
6667
				po.oprcode::pg_catalog.regproc AS oprcode,
6668
				po.oprrest::pg_catalog.regproc AS oprrest,
6669
				po.oprjoin::pg_catalog.regproc AS oprjoin
6670
			FROM
6671
				pg_catalog.pg_operator po
6672
			WHERE
6673
				po.oid='{$operator_oid}'
6674
		";
6675
6676
        return $this->selectSet($sql);
6677
    }
6678
6679
    /**
6680
     *  Gets all opclasses.
6681
     *
6682
     * @return A recordset
6683
     */
6684
    public function getOpClasses()
6685
    {
6686
        $c_schema = $this->_schema;
6687
        $this->clean($c_schema);
6688
        $sql = "
6689
			SELECT
6690
				pa.amname, po.opcname,
6691
				po.opcintype::pg_catalog.regtype AS opcintype,
6692
				po.opcdefault,
6693
				pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
6694
			FROM
6695
				pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
6696
			WHERE
6697
				po.opcmethod=pa.oid
6698
				AND po.opcnamespace=pn.oid
6699
				AND pn.nspname='{$c_schema}'
6700
			ORDER BY 1,2
6701
			";
6702
6703
        return $this->selectSet($sql);
6704
    }
6705
6706
    /**
6707
     * Creates a new FTS configuration.
6708
     *
6709
     * @param string $cfgname  The name of the FTS configuration to create
6710
     * @param string $parser   The parser to be used in new FTS configuration
6711
     * @param string $template The existing FTS configuration to be used as template for the new one
6712
     * @param string $comment  If omitted, defaults to nothing
6713
     *
6714
     * @return bool|int 0 success
6715
     *
6716
     * @internal param string $locale Locale of the FTS configuration
6717
     * @internal param string $withmap Should we copy whole map of existing FTS configuration to the new one
6718
     * @internal param string $makeDefault Should this configuration be the default for locale given
6719
     */
6720
    public function createFtsConfiguration($cfgname, $parser = '', $template = '', $comment = '')
6721
    {
6722
        $f_schema = $this->_schema;
6723
        $this->fieldClean($f_schema);
6724
        $this->fieldClean($cfgname);
6725
6726
        $sql = "CREATE TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$cfgname}\" (";
6727 View Code Duplication
        if ($parser != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6728
            $this->fieldClean($parser['schema']);
6729
            $this->fieldClean($parser['parser']);
6730
            $parser = "\"{$parser['schema']}\".\"{$parser['parser']}\"";
6731
            $sql .= " PARSER = {$parser}";
6732
        }
6733 View Code Duplication
        if ($template != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6734
            $this->fieldClean($template['schema']);
6735
            $this->fieldClean($template['name']);
6736
            $sql .= " COPY = \"{$template['schema']}\".\"{$template['name']}\"";
6737
        }
6738
        $sql .= ')';
6739
6740
        if ($comment != '') {
6741
            $status = $this->beginTransaction();
6742
            if ($status != 0) {
6743
                return -1;
6744
            }
6745
        }
6746
6747
        // Create the FTS configuration
6748
        $status = $this->execute($sql);
6749
        if ($status != 0) {
6750
            $this->rollbackTransaction();
6751
6752
            return -1;
6753
        }
6754
6755
        // Set the comment
6756 View Code Duplication
        if ($comment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6757
            $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
6758
            if ($status != 0) {
6759
                $this->rollbackTransaction();
6760
6761
                return -1;
6762
            }
6763
6764
            return $this->endTransaction();
6765
        }
6766
6767
        return 0;
6768
    }
6769
6770
    // Language functions
6771
6772
    /**
6773
     * Returns available FTS configurations.
6774
     *
6775
     * @param bool|\PHPPgAdmin\Database\if $all if false, returns schema qualified FTS confs
6776
     *
6777
     * @return \PHPPgAdmin\Database\A recordset
6778
     */
6779
    public function getFtsConfigurations($all = true)
6780
    {
6781
        $c_schema = $this->_schema;
6782
        $this->clean($c_schema);
6783
        $sql = "
6784
			SELECT
6785
				n.nspname as schema,
6786
				c.cfgname as name,
6787
				pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
6788
			FROM
6789
				pg_catalog.pg_ts_config c
6790
				JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
6791
			WHERE
6792
				pg_catalog.pg_ts_config_is_visible(c.oid)";
6793
6794
        if (!$all) {
6795
            $sql .= " AND  n.nspname='{$c_schema}'\n";
6796
        }
6797
6798
        $sql .= 'ORDER BY name';
6799
6800
        return $this->selectSet($sql);
6801
    }
6802
6803
    // Aggregate functions
6804
6805
    /**
6806
     * Returns the map of FTS configuration given
6807
     * (list of mappings (tokens) and their processing dictionaries).
6808
     *
6809
     * @param string $ftscfg Name of the FTS configuration
6810
     *
6811
     * @return RecordSet
6812
     */
6813
    public function getFtsConfigurationMap($ftscfg)
6814
    {
6815
        $c_schema = $this->_schema;
6816
        $this->clean($c_schema);
6817
        $this->fieldClean($ftscfg);
6818
6819
        $oidSet = $this->selectSet("SELECT c.oid
6820
			FROM pg_catalog.pg_ts_config AS c
6821
				LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.cfgnamespace)
6822
			WHERE c.cfgname = '{$ftscfg}'
6823
				AND n.nspname='{$c_schema}'");
6824
6825
        $oid = $oidSet->fields['oid'];
6826
6827
        $sql = "
6828
 			SELECT
6829
    			(SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
6830
        		(SELECT t.description FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS description,
6831
				c.cfgname AS cfgname, n.nspname ||'.'|| d.dictname as dictionaries
6832
			FROM
6833
				pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d,
6834
				pg_catalog.pg_namespace n
6835
			WHERE
6836
				c.oid = {$oid}
6837
				AND m.mapcfg = c.oid
6838
				AND m.mapdict = d.oid
6839
				AND d.dictnamespace = n.oid
6840
			ORDER BY name
6841
			";
6842
6843
        return $this->selectSet($sql);
6844
    }
6845
6846
    /**
6847
     * Returns FTS parsers available.
6848
     *
6849
     * @param bool|\PHPPgAdmin\Database\if $all if false, return only Parsers from the current schema
6850
     *
6851
     * @return \PHPPgAdmin\Database\RecordSet
6852
     */
6853
    public function getFtsParsers($all = true)
6854
    {
6855
        $c_schema = $this->_schema;
6856
        $this->clean($c_schema);
6857
        $sql = "
6858
			SELECT
6859
			   n.nspname as schema,
6860
			   p.prsname as name,
6861
			   pg_catalog.obj_description(p.oid, 'pg_ts_parser') as comment
6862
			FROM pg_catalog.pg_ts_parser p
6863
				LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = p.prsnamespace)
6864
			WHERE pg_catalog.pg_ts_parser_is_visible(p.oid)";
6865
6866
        if (!$all) {
6867
            $sql .= " AND n.nspname='{$c_schema}'\n";
6868
        }
6869
6870
        $sql .= 'ORDER BY name';
6871
6872
        return $this->selectSet($sql);
6873
    }
6874
6875
    /**
6876
     * Returns FTS dictionaries available.
6877
     *
6878
     * @param bool|\PHPPgAdmin\Database\if $all if false, return only Dics from the current schema
6879
     *
6880
     * @return \PHPPgAdmin\Database\RecordSet
6881
     */
6882
    public function getFtsDictionaries($all = true)
6883
    {
6884
        $c_schema = $this->_schema;
6885
        $this->clean($c_schema);
6886
        $sql = "
6887
 			SELECT
6888
				n.nspname as schema, d.dictname as name,
6889
				pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
6890
			FROM pg_catalog.pg_ts_dict d
6891
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
6892
			WHERE pg_catalog.pg_ts_dict_is_visible(d.oid)";
6893
6894
        if (!$all) {
6895
            $sql .= " AND n.nspname='{$c_schema}'\n";
6896
        }
6897
6898
        $sql .= 'ORDER BY name;';
6899
6900
        return $this->selectSet($sql);
6901
    }
6902
6903
    /**
6904
     * Returns all FTS dictionary templates available.
6905
     */
6906
    public function getFtsDictionaryTemplates()
6907
    {
6908
        $sql = "
6909
 			SELECT
6910
				n.nspname as schema,
6911
				t.tmplname as name,
6912
				( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname
6913
					FROM pg_catalog.pg_proc p
6914
					LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
6915
					WHERE t.tmplinit = p.oid ) AS  init,
6916
				( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname
6917
					FROM pg_catalog.pg_proc p
6918
					LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
6919
					WHERE t.tmpllexize = p.oid ) AS  lexize,
6920
				pg_catalog.obj_description(t.oid, 'pg_ts_template') as comment
6921
			FROM pg_catalog.pg_ts_template t
6922
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace
6923
			WHERE pg_catalog.pg_ts_template_is_visible(t.oid)
6924
			ORDER BY name;";
6925
6926
        return $this->selectSet($sql);
6927
    }
6928
6929
    /**
6930
     * Drops FTS coniguration.
6931
     *
6932
     * @param $ftscfg  The configuration's name
6933
     * @param $cascade Cascade to dependenced objects
6934
     *
6935
     * @return \PHPPgAdmin\Database\A 0 on success
6936
     */
6937 View Code Duplication
    public function dropFtsConfiguration($ftscfg, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6938
    {
6939
        $f_schema = $this->_schema;
6940
        $this->fieldClean($f_schema);
6941
        $this->fieldClean($ftscfg);
6942
6943
        $sql = "DROP TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\"";
6944
        if ($cascade) {
6945
            $sql .= ' CASCADE';
6946
        }
6947
6948
        return $this->execute($sql);
6949
    }
6950
6951
    /**
6952
     * Drops FTS dictionary.
6953
     *
6954
     * @param $ftsdict The dico's name
6955
     * @param $cascade Cascade to dependenced objects
6956
     *
6957
     * @return \PHPPgAdmin\Database\A 0 on success
6958
     *
6959
     * @todo Support of dictionary templates dropping
6960
     */
6961 View Code Duplication
    public function dropFtsDictionary($ftsdict, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6962
    {
6963
        $f_schema = $this->_schema;
6964
        $this->fieldClean($f_schema);
6965
        $this->fieldClean($ftsdict);
6966
6967
        $sql = 'DROP TEXT SEARCH DICTIONARY';
6968
        $sql .= " \"{$f_schema}\".\"{$ftsdict}\"";
6969
        if ($cascade) {
6970
            $sql .= ' CASCADE';
6971
        }
6972
6973
        return $this->execute($sql);
6974
    }
6975
6976
    /**
6977
     * Alters FTS configuration.
6978
     *
6979
     * @param $cfgname The conf's name
6980
     * @param $comment A comment on for the conf
6981
     * @param $name    The new conf name
6982
     *
6983
     * @return bool|int 0 on success
6984
     */
6985 View Code Duplication
    public function updateFtsConfiguration($cfgname, $comment, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
6986
    {
6987
        $status = $this->beginTransaction();
6988
        if ($status != 0) {
6989
            $this->rollbackTransaction();
6990
6991
            return -1;
6992
        }
6993
6994
        $this->fieldClean($cfgname);
6995
6996
        $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
6997
        if ($status != 0) {
6998
            $this->rollbackTransaction();
6999
7000
            return -1;
7001
        }
7002
7003
        // Only if the name has changed
7004
        if ($name != $cfgname) {
7005
            $f_schema = $this->_schema;
7006
            $this->fieldClean($f_schema);
7007
            $this->fieldClean($name);
7008
7009
            $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$cfgname}\" RENAME TO \"{$name}\"";
7010
            $status = $this->execute($sql);
7011
            if ($status != 0) {
7012
                $this->rollbackTransaction();
7013
7014
                return -1;
7015
            }
7016
        }
7017
7018
        return $this->endTransaction();
7019
    }
7020
7021
    /**
7022
     * Creates a new FTS dictionary or FTS dictionary template.
7023
     *
7024
     * @param string $dictname   The name of the FTS dictionary to create
7025
     * @param bool   $isTemplate Flag whether we create usual dictionary or dictionary template
7026
     * @param string $template   The existing FTS dictionary to be used as template for the new one
7027
     * @param string $lexize     The name of the function, which does transformation of input word
7028
     * @param string $init       The name of the function, which initializes dictionary
7029
     * @param string $option     Usually, it stores various options required for the dictionary
7030
     * @param string $comment    If omitted, defaults to nothing
7031
     *
7032
     * @return bool|int 0 success
7033
     */
7034
    public function createFtsDictionary(
7035
        $dictname,
7036
        $isTemplate = false,
7037
        $template = '',
7038
        $lexize = '',
7039
        $init = '',
7040
        $option = '',
7041
        $comment = ''
7042
    ) {
7043
        $f_schema = $this->_schema;
7044
        $this->fieldClean($f_schema);
7045
        $this->fieldClean($dictname);
7046
        $this->fieldClean($template);
7047
        $this->fieldClean($lexize);
7048
        $this->fieldClean($init);
7049
        $this->fieldClean($option);
7050
7051
        $sql = 'CREATE TEXT SEARCH';
7052
        if ($isTemplate) {
7053
            $sql .= " TEMPLATE \"{$f_schema}\".\"{$dictname}\" (";
7054
            if ($lexize != '') {
7055
                $sql .= " LEXIZE = {$lexize}";
7056
            }
7057
7058
            if ($init != '') {
7059
                $sql .= ", INIT = {$init}";
7060
            }
7061
7062
            $sql .= ')';
7063
            $whatToComment = 'TEXT SEARCH TEMPLATE';
7064
        } else {
7065
            $sql .= " DICTIONARY \"{$f_schema}\".\"{$dictname}\" (";
7066 View Code Duplication
            if ($template != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7067
                $this->fieldClean($template['schema']);
7068
                $this->fieldClean($template['name']);
7069
                $template = "\"{$template['schema']}\".\"{$template['name']}\"";
7070
7071
                $sql .= " TEMPLATE = {$template}";
7072
            }
7073
            if ($option != '') {
7074
                $sql .= ", {$option}";
7075
            }
7076
7077
            $sql .= ')';
7078
            $whatToComment = 'TEXT SEARCH DICTIONARY';
7079
        }
7080
7081
        /* if comment, begin a transaction to
7082
         * run both commands */
7083
        if ($comment != '') {
7084
            $status = $this->beginTransaction();
7085
            if ($status != 0) {
7086
                return -1;
7087
            }
7088
        }
7089
7090
        // Create the FTS dictionary
7091
        $status = $this->execute($sql);
7092
        if ($status != 0) {
7093
            $this->rollbackTransaction();
7094
7095
            return -1;
7096
        }
7097
7098
        // Set the comment
7099 View Code Duplication
        if ($comment != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7100
            $status = $this->setComment($whatToComment, $dictname, '', $comment);
7101
            if ($status != 0) {
7102
                $this->rollbackTransaction();
7103
7104
                return -1;
7105
            }
7106
        }
7107
7108
        return $this->endTransaction();
7109
    }
7110
7111
    // Role, User/Group functions
7112
7113
    /**
7114
     * Alters FTS dictionary or dictionary template.
7115
     *
7116
     * @param $dictname The dico's name
7117
     * @param $comment  The comment
7118
     * @param $name     The new dico's name
7119
     *
7120
     * @return bool|int 0 on success
7121
     */
7122 View Code Duplication
    public function updateFtsDictionary($dictname, $comment, $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7123
    {
7124
        $status = $this->beginTransaction();
7125
        if ($status != 0) {
7126
            $this->rollbackTransaction();
7127
7128
            return -1;
7129
        }
7130
7131
        $this->fieldClean($dictname);
7132
        $status = $this->setComment('TEXT SEARCH DICTIONARY', $dictname, '', $comment);
7133
        if ($status != 0) {
7134
            $this->rollbackTransaction();
7135
7136
            return -1;
7137
        }
7138
7139
        // Only if the name has changed
7140
        if ($name != $dictname) {
7141
            $f_schema = $this->_schema;
7142
            $this->fieldClean($f_schema);
7143
            $this->fieldClean($name);
7144
7145
            $sql = "ALTER TEXT SEARCH DICTIONARY \"{$f_schema}\".\"{$dictname}\" RENAME TO \"{$name}\"";
7146
            $status = $this->execute($sql);
7147
            if ($status != 0) {
7148
                $this->rollbackTransaction();
7149
7150
                return -1;
7151
            }
7152
        }
7153
7154
        return $this->endTransaction();
7155
    }
7156
7157
    /**
7158
     * Return all information relating to a FTS dictionary.
7159
     *
7160
     * @param $ftsdict The name of the FTS dictionary
7161
     *
7162
     * @return RecordSet of FTS dictionary information
7163
     */
7164
    public function getFtsDictionaryByName($ftsdict)
7165
    {
7166
        $c_schema = $this->_schema;
7167
        $this->clean($c_schema);
7168
        $this->clean($ftsdict);
7169
7170
        $sql = "SELECT
7171
			   n.nspname as schema,
7172
			   d.dictname as name,
7173
			   ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM
7174
				 pg_catalog.pg_ts_template t
7175
									  LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace
7176
									  WHERE d.dicttemplate = t.oid ) AS  template,
7177
			   d.dictinitoption as init,
7178
			   pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
7179
			FROM pg_catalog.pg_ts_dict d
7180
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
7181
			WHERE d.dictname = '{$ftsdict}'
7182
			   AND pg_catalog.pg_ts_dict_is_visible(d.oid)
7183
			   AND n.nspname='{$c_schema}'
7184
			ORDER BY name";
7185
7186
        return $this->selectSet($sql);
7187
    }
7188
7189
    /**
7190
     * Creates/updates/deletes FTS mapping.
7191
     *
7192
     * @param        $ftscfg
7193
     * @param array  $mapping  Array of tokens' names
7194
     * @param string $action   What to do with the mapping: add, alter or drop
7195
     * @param string $dictname Dictionary that will process tokens given or null in case of drop action
7196
     *
7197
     * @return int|\PHPPgAdmin\Database\A 0 success
7198
     *
7199
     * @internal param string $cfgname The name of the FTS configuration to alter
7200
     */
7201
    public function changeFtsMapping($ftscfg, $mapping, $action, $dictname = null)
7202
    {
7203
        if (count($mapping) > 0) {
7204
            $f_schema = $this->_schema;
7205
            $this->fieldClean($f_schema);
7206
            $this->fieldClean($ftscfg);
7207
            $this->fieldClean($dictname);
7208
            $this->arrayClean($mapping);
7209
7210
            switch ($action) {
7211
                case 'alter':
7212
                    $whatToDo = 'ALTER';
7213
                    break;
7214
                case 'drop':
7215
                    $whatToDo = 'DROP';
7216
                    break;
7217
                default:
7218
                    $whatToDo = 'ADD';
7219
                    break;
7220
            }
7221
7222
            $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\" {$whatToDo} MAPPING FOR ";
7223
            $sql .= implode(',', $mapping);
7224
            if ($action != 'drop' && !empty($dictname)) {
7225
                $sql .= " WITH {$dictname}";
7226
            }
7227
7228
            return $this->execute($sql);
7229
        }
7230
7231
        return -1;
7232
    }
7233
7234
    /**
7235
     * Return all information related to a given FTS configuration's mapping.
7236
     *
7237
     * @param $ftscfg  The name of the FTS configuration
7238
     * @param $mapping The name of the mapping
7239
     *
7240
     * @return FTS configuration information
7241
     */
7242
    public function getFtsMappingByName($ftscfg, $mapping)
7243
    {
7244
        $c_schema = $this->_schema;
7245
        $this->clean($c_schema);
7246
        $this->clean($ftscfg);
7247
        $this->clean($mapping);
7248
7249
        $oidSet = $this->selectSet("SELECT c.oid, cfgparser
7250
			FROM pg_catalog.pg_ts_config AS c
7251
				LEFT JOIN pg_catalog.pg_namespace AS n ON n.oid = c.cfgnamespace
7252
			WHERE c.cfgname = '{$ftscfg}'
7253
				AND n.nspname='{$c_schema}'");
7254
7255
        $oid = $oidSet->fields['oid'];
7256
        $cfgparser = $oidSet->fields['cfgparser'];
7257
7258
        $tokenIdSet = $this->selectSet("SELECT tokid
7259
			FROM pg_catalog.ts_token_type({$cfgparser})
7260
			WHERE alias = '{$mapping}'");
7261
7262
        $tokid = $tokenIdSet->fields['tokid'];
7263
7264
        $sql = "SELECT
7265
			    (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
7266
    	            d.dictname as dictionaries
7267
			FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d
7268
			WHERE c.oid = {$oid} AND m.mapcfg = c.oid AND m.maptokentype = {$tokid} AND m.mapdict = d.oid
7269
			LIMIT 1;";
7270
7271
        return $this->selectSet($sql);
7272
    }
7273
7274
    /**
7275
     * Return list of FTS mappings possible for given parser
7276
     * (specified by given configuration since configuration can only have 1 parser).
7277
     *
7278
     * @param $ftscfg The config's name that use the parser
7279
     *
7280
     * @return \PHPPgAdmin\Database\A 0 on success
7281
     */
7282
    public function getFtsMappings($ftscfg)
7283
    {
7284
        $cfg = $this->getFtsConfigurationByName($ftscfg);
7285
7286
        $sql = "SELECT alias AS name, description
7287
			FROM pg_catalog.ts_token_type({$cfg->fields['parser_id']})
7288
			ORDER BY name";
7289
7290
        return $this->selectSet($sql);
7291
    }
7292
7293
    /**
7294
     * Return all information related to a FTS configuration.
7295
     *
7296
     * @param $ftscfg The name of the FTS configuration
7297
     *
7298
     * @return FTS configuration information
7299
     */
7300
    public function getFtsConfigurationByName($ftscfg)
7301
    {
7302
        $c_schema = $this->_schema;
7303
        $this->clean($c_schema);
7304
        $this->clean($ftscfg);
7305
        $sql = "
7306
			SELECT
7307
				n.nspname as schema,
7308
				c.cfgname as name,
7309
				p.prsname as parser,
7310
				c.cfgparser as parser_id,
7311
				pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
7312
			FROM pg_catalog.pg_ts_config c
7313
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
7314
				LEFT JOIN pg_catalog.pg_ts_parser p ON p.oid = c.cfgparser
7315
			WHERE pg_catalog.pg_ts_config_is_visible(c.oid)
7316
				AND c.cfgname = '{$ftscfg}'
7317
				AND n.nspname='{$c_schema}'";
7318
7319
        return $this->selectSet($sql);
7320
    }
7321
7322
    /**
7323
     * Gets all languages.
7324
     *
7325
     * @param bool|true $all True to get all languages, regardless of show_system
7326
     *
7327
     * @return \PHPPgAdmin\Database\A recordset
7328
     */
7329 View Code Duplication
    public function getLanguages($all = false)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7330
    {
7331
        $conf = $this->conf;
7332
7333
        if ($conf['show_system'] || $all) {
7334
            $where = '';
7335
        } else {
7336
            $where = 'WHERE lanispl';
7337
        }
7338
7339
        $sql = "
7340
			SELECT
7341
				lanname, lanpltrusted,
7342
				lanplcallfoid::pg_catalog.regproc AS lanplcallf
7343
			FROM
7344
				pg_catalog.pg_language
7345
			{$where}
7346
			ORDER BY lanname
7347
		";
7348
7349
        return $this->selectSet($sql);
7350
    }
7351
7352
    /**
7353
     * Creates a new aggregate in the database.
7354
     *
7355
     * @param $name     The name of the aggregate
7356
     * @param $basetype The input data type of the aggregate
7357
     * @param $sfunc    The name of the state transition function for the aggregate
7358
     * @param $stype    The data type for the aggregate's state value
7359
     * @param $ffunc    The name of the final function for the aggregate
7360
     * @param $initcond The initial setting for the state value
7361
     * @param $sortop   The sort operator for the aggregate
7362
     * @param $comment  Aggregate comment
7363
     *
7364
     * @return bool|int 0 success
7365
     */
7366
    public function createAggregate($name, $basetype, $sfunc, $stype, $ffunc, $initcond, $sortop, $comment)
7367
    {
7368
        $f_schema = $this->_schema;
7369
        $this->fieldClean($f_schema);
7370
        $this->fieldClean($name);
7371
        $this->fieldClean($basetype);
7372
        $this->fieldClean($sfunc);
7373
        $this->fieldClean($stype);
7374
        $this->fieldClean($ffunc);
7375
        $this->fieldClean($initcond);
7376
        $this->fieldClean($sortop);
7377
7378
        $this->beginTransaction();
7379
7380
        $sql = "CREATE AGGREGATE \"{$f_schema}\".\"{$name}\" (BASETYPE = \"{$basetype}\", SFUNC = \"{$sfunc}\", STYPE = \"{$stype}\"";
7381
        if (trim($ffunc) != '') {
7382
            $sql .= ", FINALFUNC = \"{$ffunc}\"";
7383
        }
7384
7385
        if (trim($initcond) != '') {
7386
            $sql .= ", INITCOND = \"{$initcond}\"";
7387
        }
7388
7389
        if (trim($sortop) != '') {
7390
            $sql .= ", SORTOP = \"{$sortop}\"";
7391
        }
7392
7393
        $sql .= ')';
7394
7395
        $status = $this->execute($sql);
7396
        if ($status) {
7397
            $this->rollbackTransaction();
7398
7399
            return -1;
7400
        }
7401
7402 View Code Duplication
        if (trim($comment) != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7403
            $status = $this->setComment('AGGREGATE', $name, '', $comment, $basetype);
7404
            if ($status) {
7405
                $this->rollbackTransaction();
7406
7407
                return -1;
7408
            }
7409
        }
7410
7411
        return $this->endTransaction();
7412
    }
7413
7414
    /**
7415
     * Removes an aggregate function from the database.
7416
     *
7417
     * @param $aggrname The name of the aggregate
7418
     * @param $aggrtype The input data type of the aggregate
7419
     * @param $cascade  True to cascade drop, false to restrict
7420
     *
7421
     * @return \PHPPgAdmin\Database\A 0 success
7422
     */
7423 View Code Duplication
    public function dropAggregate($aggrname, $aggrtype, $cascade)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7424
    {
7425
        $f_schema = $this->_schema;
7426
        $this->fieldClean($f_schema);
7427
        $this->fieldClean($aggrname);
7428
        $this->fieldClean($aggrtype);
7429
7430
        $sql = "DROP AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\")";
7431
        if ($cascade) {
7432
            $sql .= ' CASCADE';
7433
        }
7434
7435
        return $this->execute($sql);
7436
    }
7437
7438
    /**
7439
     * Gets all information for an aggregate.
7440
     *
7441
     * @param $name     The name of the aggregate
7442
     * @param $basetype The input data type of the aggregate
7443
     *
7444
     * @return A recordset
7445
     */
7446
    public function getAggregate($name, $basetype)
7447
    {
7448
        $c_schema = $this->_schema;
7449
        $this->clean($c_schema);
7450
        $this->fieldClean($name);
7451
        $this->fieldClean($basetype);
7452
7453
        $sql = "
7454
			SELECT p.proname, CASE p.proargtypes[0]
7455
				WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL
7456
				ELSE pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes,
7457
				a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn,
7458
				a.agginitval, a.aggsortop, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
7459
			FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
7460
			WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
7461
				AND p.proisagg AND n.nspname='{$c_schema}'
7462
				AND p.proname='".$name."'
7463
				AND CASE p.proargtypes[0]
7464
					WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN ''
7465
					ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
7466
				END ='".$basetype."'";
7467
7468
        return $this->selectSet($sql);
7469
    }
7470
7471
    /**
7472
     * Gets all aggregates.
7473
     *
7474
     * @return A recordset
7475
     */
7476
    public function getAggregates()
7477
    {
7478
        $c_schema = $this->_schema;
7479
        $this->clean($c_schema);
7480
        $sql = "SELECT p.proname, CASE p.proargtypes[0] WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL ELSE
7481
			   pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes, a.aggtransfn, u.usename,
7482
			   pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
7483
			   FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
7484
			   WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
7485
			   AND p.proisagg AND n.nspname='{$c_schema}' ORDER BY 1, 2";
7486
7487
        return $this->selectSet($sql);
7488
    }
7489
7490
    /**
7491
     * Alters an aggregate.
7492
     *
7493
     * @param $aggrname       The actual name of the aggregate
7494
     * @param $aggrtype       The actual input data type of the aggregate
7495
     * @param $aggrowner      The actual owner of the aggregate
7496
     * @param $aggrschema     The actual schema the aggregate belongs to
7497
     * @param $aggrcomment    The actual comment for the aggregate
7498
     * @param $newaggrname    The new name of the aggregate
7499
     * @param $newaggrowner   The new owner of the aggregate
7500
     * @param $newaggrschema  The new schema where the aggregate will belong to
7501
     * @param $newaggrcomment The new comment for the aggregate
7502
     *
7503
     * @return bool|int 0 success
7504
     */
7505
    public function alterAggregate(
7506
        $aggrname,
7507
        $aggrtype,
7508
        $aggrowner,
7509
        $aggrschema,
7510
        $aggrcomment,
7511
        $newaggrname,
7512
        $newaggrowner,
7513
        $newaggrschema,
7514
        $newaggrcomment
7515
    ) {
7516
        // Clean fields
7517
        $this->fieldClean($aggrname);
7518
        $this->fieldClean($aggrtype);
7519
        $this->fieldClean($aggrowner);
7520
        $this->fieldClean($aggrschema);
7521
        $this->fieldClean($newaggrname);
7522
        $this->fieldClean($newaggrowner);
7523
        $this->fieldClean($newaggrschema);
7524
7525
        $this->beginTransaction();
7526
7527
        // Change the owner, if it has changed
7528
        if ($aggrowner != $newaggrowner) {
7529
            $status = $this->changeAggregateOwner($aggrname, $aggrtype, $newaggrowner);
7530
            if ($status != 0) {
7531
                $this->rollbackTransaction();
7532
7533
                return -1;
7534
            }
7535
        }
7536
7537
        // Set the comment, if it has changed
7538
        if ($aggrcomment != $newaggrcomment) {
7539
            $status = $this->setComment('AGGREGATE', $aggrname, '', $newaggrcomment, $aggrtype);
7540
            if ($status) {
7541
                $this->rollbackTransaction();
7542
7543
                return -2;
7544
            }
7545
        }
7546
7547
        // Change the schema, if it has changed
7548
        if ($aggrschema != $newaggrschema) {
7549
            $status = $this->changeAggregateSchema($aggrname, $aggrtype, $newaggrschema);
7550
            if ($status != 0) {
7551
                $this->rollbackTransaction();
7552
7553
                return -3;
7554
            }
7555
        }
7556
7557
        // Rename the aggregate, if it has changed
7558
        if ($aggrname != $newaggrname) {
7559
            $status = $this->renameAggregate($newaggrschema, $aggrname, $aggrtype, $newaggrname);
7560
            if ($status != 0) {
7561
                $this->rollbackTransaction();
7562
7563
                return -4;
7564
            }
7565
        }
7566
7567
        return $this->endTransaction();
7568
    }
7569
7570
    /**
7571
     * Changes the owner of an aggregate function.
7572
     *
7573
     * @param $aggrname     The name of the aggregate
7574
     * @param $aggrtype     The input data type of the aggregate
7575
     * @param $newaggrowner The new owner of the aggregate
7576
     *
7577
     * @return \PHPPgAdmin\Database\A 0 success
7578
     */
7579 View Code Duplication
    public function changeAggregateOwner($aggrname, $aggrtype, $newaggrowner)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7580
    {
7581
        $f_schema = $this->_schema;
7582
        $this->fieldClean($f_schema);
7583
        $this->fieldClean($aggrname);
7584
        $this->fieldClean($newaggrowner);
7585
        $sql = "ALTER AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") OWNER TO \"{$newaggrowner}\"";
7586
7587
        return $this->execute($sql);
7588
    }
7589
7590
    /**
7591
     * Changes the schema of an aggregate function.
7592
     *
7593
     * @param $aggrname      The name of the aggregate
7594
     * @param $aggrtype      The input data type of the aggregate
7595
     * @param $newaggrschema The new schema for the aggregate
7596
     *
7597
     * @return \PHPPgAdmin\Database\A 0 success
7598
     */
7599 View Code Duplication
    public function changeAggregateSchema($aggrname, $aggrtype, $newaggrschema)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7600
    {
7601
        $f_schema = $this->_schema;
7602
        $this->fieldClean($f_schema);
7603
        $this->fieldClean($aggrname);
7604
        $this->fieldClean($newaggrschema);
7605
        $sql = "ALTER AGGREGATE \"{$f_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") SET SCHEMA  \"{$newaggrschema}\"";
7606
7607
        return $this->execute($sql);
7608
    }
7609
7610
    /**
7611
     * Renames an aggregate function.
7612
     *
7613
     * @param $aggrschema
7614
     * @param $aggrname    The actual name of the aggregate
7615
     * @param $aggrtype    The actual input data type of the aggregate
7616
     * @param $newaggrname The new name of the aggregate
7617
     *
7618
     * @return \PHPPgAdmin\Database\A 0 success
7619
     */
7620
    public function renameAggregate($aggrschema, $aggrname, $aggrtype, $newaggrname)
7621
    {
7622
        /* this function is called from alterAggregate where params are cleaned */
7623
        $sql = "ALTER AGGREGATE \"{$aggrschema}\"".'.'."\"{$aggrname}\" (\"{$aggrtype}\") RENAME TO \"{$newaggrname}\"";
7624
7625
        return $this->execute($sql);
7626
    }
7627
7628
    /**
7629
     * Returns all roles in the database cluster.
7630
     *
7631
     * @param $rolename (optional) The role name to exclude from the select
7632
     *
7633
     * @return All roles
7634
     */
7635
    public function getRoles($rolename = '')
7636
    {
7637
        $sql = '
7638
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
7639
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
7640
			FROM pg_catalog.pg_roles';
7641
        if ($rolename) {
7642
            $sql .= " WHERE rolname!='{$rolename}'";
7643
        }
7644
7645
        $sql .= ' ORDER BY rolname';
7646
7647
        return $this->selectSet($sql);
7648
    }
7649
7650
    /**
7651
     * Returns information about a single role.
7652
     *
7653
     * @param $rolename The name of the role to retrieve
7654
     *
7655
     * @return The role's data
7656
     */
7657
    public function getRole($rolename)
7658
    {
7659
        $this->clean($rolename);
7660
7661
        $sql = "
7662
			SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
7663
				rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
7664
			FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'";
7665
7666
        return $this->selectSet($sql);
7667
    }
7668
7669
    /**
7670
     * Returns all users in the database cluster.
7671
     *
7672
     * @return All users
7673
     */
7674
    public function getUsers()
7675
    {
7676
        $sql = 'SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
7677
			FROM pg_user
7678
			ORDER BY usename';
7679
7680
        return $this->selectSet($sql);
7681
    }
7682
7683
    /**
7684
     * Returns information about a single user.
7685
     *
7686
     * @param $username The username of the user to retrieve
7687
     *
7688
     * @return The user's data
7689
     */
7690
    public function getUser($username)
7691
    {
7692
        $this->clean($username);
7693
7694
        $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires, useconfig
7695
			FROM pg_user
7696
			WHERE usename='{$username}'";
7697
7698
        return $this->selectSet($sql);
7699
    }
7700
7701
    /**
7702
     * Creates a new role.
7703
     *
7704
     * @param $rolename     The name of the role to create
7705
     * @param $password     A password for the role
7706
     * @param $superuser    Boolean whether or not the role is a superuser
7707
     * @param $createdb     Boolean whether or not the role can create databases
7708
     * @param $createrole   Boolean whether or not the role can create other roles
7709
     * @param $inherits     Boolean whether or not the role inherits the privileges from parent roles
7710
     * @param $login        Boolean whether or not the role will be allowed to login
7711
     * @param $connlimit    Number of concurrent connections the role can make
7712
     * @param $expiry       String Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
7713
     * @param $memberof     (array) Roles to which the new role will be immediately added as a new member
7714
     * @param $members      (array) Roles which are automatically added as members of the new role
7715
     * @param $adminmembers (array) Roles which are automatically added as admin members of the new role
7716
     *
7717
     * @return \PHPPgAdmin\Database\A 0 success
7718
     */
7719
    public function createRole(
7720
        $rolename,
7721
        $password,
7722
        $superuser,
7723
        $createdb,
7724
        $createrole,
7725
        $inherits,
7726
        $login,
7727
        $connlimit,
7728
        $expiry,
7729
        $memberof,
7730
        $members,
7731
        $adminmembers
7732
    ) {
7733
        $enc = $this->_encryptPassword($rolename, $password);
7734
        $this->fieldClean($rolename);
7735
        $this->clean($enc);
7736
        $this->clean($connlimit);
7737
        $this->clean($expiry);
7738
        $this->fieldArrayClean($memberof);
7739
        $this->fieldArrayClean($members);
7740
        $this->fieldArrayClean($adminmembers);
7741
7742
        $sql = "CREATE ROLE \"{$rolename}\"";
7743
        if ($password != '') {
7744
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
7745
        }
7746
7747
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
7748
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
7749
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
7750
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
7751
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
7752
        if ($connlimit != '') {
7753
            $sql .= " CONNECTION LIMIT {$connlimit}";
7754
        } else {
7755
            $sql .= ' CONNECTION LIMIT -1';
7756
        }
7757
7758
        if ($expiry != '') {
7759
            $sql .= " VALID UNTIL '{$expiry}'";
7760
        } else {
7761
            $sql .= " VALID UNTIL 'infinity'";
7762
        }
7763
7764
        if (is_array($memberof) && count($memberof) > 0) {
7765
            $sql .= ' IN ROLE "'.implode('", "', $memberof).'"';
7766
        }
7767
7768
        if (is_array($members) && count($members) > 0) {
7769
            $sql .= ' ROLE "'.implode('", "', $members).'"';
7770
        }
7771
7772
        if (is_array($adminmembers) && count($adminmembers) > 0) {
7773
            $sql .= ' ADMIN "'.implode('", "', $adminmembers).'"';
7774
        }
7775
7776
        return $this->execute($sql);
7777
    }
7778
7779
    /**
7780
     * Helper function that computes encypted PostgreSQL passwords.
7781
     *
7782
     * @param $username The username
7783
     * @param $password The password
7784
     *
7785
     * @return string
7786
     */
7787
    public function _encryptPassword($username, $password)
7788
    {
7789
        return 'md5'.md5($password.$username);
7790
    }
7791
7792
    /**
7793
     * Adjusts a role's info and renames it.
7794
     *
7795
     * @param $rolename        The name of the role to adjust
7796
     * @param $password        A password for the role
7797
     * @param $superuser       Boolean whether or not the role is a superuser
7798
     * @param $createdb        Boolean whether or not the role can create databases
7799
     * @param $createrole      Boolean whether or not the role can create other roles
7800
     * @param $inherits        Boolean whether or not the role inherits the privileges from parent roles
7801
     * @param $login           Boolean whether or not the role will be allowed to login
7802
     * @param $connlimit       Number of concurrent connections the role can make
7803
     * @param $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
7804
     * @param $memberof        (array) Roles to which the role will be immediately added as a new member
7805
     * @param $members         (array) Roles which are automatically added as members of the role
7806
     * @param $adminmembers    (array) Roles which are automatically added as admin members of the role
7807
     * @param $memberofold     (array) Original roles whose the role belongs to
7808
     * @param $membersold      (array) Original roles that are members of the role
7809
     * @param $adminmembersold (array) Original roles that are admin members of the role
7810
     * @param $newrolename     The new name of the role
7811
     *
7812
     * @return bool|int 0 success
7813
     */
7814
    public function setRenameRole(
7815
        $rolename,
7816
        $password,
7817
        $superuser,
7818
        $createdb,
7819
        $createrole,
7820
        $inherits,
7821
        $login,
7822
        $connlimit,
7823
        $expiry,
7824
        $memberof,
7825
        $members,
7826
        $adminmembers,
7827
        $memberofold,
7828
        $membersold,
7829
        $adminmembersold,
7830
        $newrolename
7831
    ) {
7832
        $status = $this->beginTransaction();
7833
        if ($status != 0) {
7834
            return -1;
7835
        }
7836
7837
        if ($rolename != $newrolename) {
7838
            $status = $this->renameRole($rolename, $newrolename);
7839
            if ($status != 0) {
7840
                $this->rollbackTransaction();
7841
7842
                return -3;
7843
            }
7844
            $rolename = $newrolename;
7845
        }
7846
7847
        $status =
7848
        $this->setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members,
7849
            $adminmembers, $memberofold, $membersold, $adminmembersold);
7850
        if ($status != 0) {
7851
            $this->rollbackTransaction();
7852
7853
            return -2;
7854
        }
7855
7856
        return $this->endTransaction();
7857
    }
7858
7859
    /**
7860
     * Renames a role.
7861
     *
7862
     * @param $rolename    The name of the role to rename
7863
     * @param $newrolename The new name of the role
7864
     *
7865
     * @return \PHPPgAdmin\Database\A 0 success
7866
     */
7867
    public function renameRole($rolename, $newrolename)
7868
    {
7869
        $this->fieldClean($rolename);
7870
        $this->fieldClean($newrolename);
7871
7872
        $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\"";
7873
7874
        return $this->execute($sql);
7875
    }
7876
7877
    /**
7878
     * Adjusts a role's info.
7879
     *
7880
     * @param $rolename        The name of the role to adjust
7881
     * @param $password        A password for the role
7882
     * @param $superuser       Boolean whether or not the role is a superuser
7883
     * @param $createdb        Boolean whether or not the role can create databases
7884
     * @param $createrole      Boolean whether or not the role can create other roles
7885
     * @param $inherits        Boolean whether or not the role inherits the privileges from parent roles
7886
     * @param $login           Boolean whether or not the role will be allowed to login
7887
     * @param $connlimit       Number of concurrent connections the role can make
7888
     * @param $expiry          string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
7889
     * @param $memberof        (array) Roles to which the role will be immediately added as a new member
7890
     * @param $members         (array) Roles which are automatically added as members of the role
7891
     * @param $adminmembers    (array) Roles which are automatically added as admin members of the role
7892
     * @param $memberofold     (array) Original roles whose the role belongs to
7893
     * @param $membersold      (array) Original roles that are members of the role
7894
     * @param $adminmembersold (array) Original roles that are admin members of the role
7895
     *
7896
     * @return int|\PHPPgAdmin\Database\A 0 success
7897
     */
7898
    public function setRole(
7899
        $rolename,
7900
        $password,
7901
        $superuser,
7902
        $createdb,
7903
        $createrole,
7904
        $inherits,
7905
        $login,
7906
        $connlimit,
7907
        $expiry,
7908
        $memberof,
7909
        $members,
7910
        $adminmembers,
7911
        $memberofold,
7912
        $membersold,
7913
        $adminmembersold
7914
    ) {
7915
        $enc = $this->_encryptPassword($rolename, $password);
7916
        $this->fieldClean($rolename);
7917
        $this->clean($enc);
7918
        $this->clean($connlimit);
7919
        $this->clean($expiry);
7920
        $this->fieldArrayClean($memberof);
7921
        $this->fieldArrayClean($members);
7922
        $this->fieldArrayClean($adminmembers);
7923
7924
        $sql = "ALTER ROLE \"{$rolename}\"";
7925
        if ($password != '') {
7926
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
7927
        }
7928
7929
        $sql .= $superuser ? ' SUPERUSER' : ' NOSUPERUSER';
7930
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
7931
        $sql .= $createrole ? ' CREATEROLE' : ' NOCREATEROLE';
7932
        $sql .= $inherits ? ' INHERIT' : ' NOINHERIT';
7933
        $sql .= $login ? ' LOGIN' : ' NOLOGIN';
7934
        if ($connlimit != '') {
7935
            $sql .= " CONNECTION LIMIT {$connlimit}";
7936
        } else {
7937
            $sql .= ' CONNECTION LIMIT -1';
7938
        }
7939
7940
        if ($expiry != '') {
7941
            $sql .= " VALID UNTIL '{$expiry}'";
7942
        } else {
7943
            $sql .= " VALID UNTIL 'infinity'";
7944
        }
7945
7946
        $status = $this->execute($sql);
7947
7948
        if ($status != 0) {
7949
            return -1;
7950
        }
7951
7952
        //memberof
7953
        $old = explode(',', $memberofold);
7954
        foreach ($memberof as $m) {
7955
            if (!in_array($m, $old)) {
7956
                $status = $this->grantRole($m, $rolename);
7957
                if ($status != 0) {
7958
                    return -1;
7959
                }
7960
            }
7961
        }
7962 View Code Duplication
        if ($memberofold) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7963
            foreach ($old as $o) {
7964
                if (!in_array($o, $memberof)) {
7965
                    $status = $this->revokeRole($o, $rolename, 0, 'CASCADE');
7966
                    if ($status != 0) {
7967
                        return -1;
7968
                    }
7969
                }
7970
            }
7971
        }
7972
7973
        //members
7974
        $old = explode(',', $membersold);
7975
        foreach ($members as $m) {
7976
            if (!in_array($m, $old)) {
7977
                $status = $this->grantRole($rolename, $m);
7978
                if ($status != 0) {
7979
                    return -1;
7980
                }
7981
            }
7982
        }
7983 View Code Duplication
        if ($membersold) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
7984
            foreach ($old as $o) {
7985
                if (!in_array($o, $members)) {
7986
                    $status = $this->revokeRole($rolename, $o, 0, 'CASCADE');
7987
                    if ($status != 0) {
7988
                        return -1;
7989
                    }
7990
                }
7991
            }
7992
        }
7993
7994
        //adminmembers
7995
        $old = explode(',', $adminmembersold);
7996
        foreach ($adminmembers as $m) {
7997
            if (!in_array($m, $old)) {
7998
                $status = $this->grantRole($rolename, $m, 1);
7999
                if ($status != 0) {
8000
                    return -1;
8001
                }
8002
            }
8003
        }
8004 View Code Duplication
        if ($adminmembersold) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8005
            foreach ($old as $o) {
8006
                if (!in_array($o, $adminmembers)) {
8007
                    $status = $this->revokeRole($rolename, $o, 1, 'CASCADE');
8008
                    if ($status != 0) {
8009
                        return -1;
8010
                    }
8011
                }
8012
            }
8013
        }
8014
8015
        return $status;
8016
    }
8017
8018
    /**
8019
     * Grants membership in a role.
8020
     *
8021
     * @param     $role     The name of the target role
8022
     * @param     $rolename The name of the role that will belong to the target role
8023
     * @param int $admin    (optional) Flag to grant the admin option
8024
     *
8025
     * @return \PHPPgAdmin\Database\A 0 success
8026
     */
8027 View Code Duplication
    public function grantRole($role, $rolename, $admin = 0)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8028
    {
8029
        $this->fieldClean($role);
8030
        $this->fieldClean($rolename);
8031
8032
        $sql = "GRANT \"{$role}\" TO \"{$rolename}\"";
8033
        if ($admin == 1) {
8034
            $sql .= ' WITH ADMIN OPTION';
8035
        }
8036
8037
        return $this->execute($sql);
8038
    }
8039
8040
    /**
8041
     * Revokes membership in a role.
8042
     *
8043
     * @param        $role     The name of the target role
8044
     * @param        $rolename The name of the role that will not belong to the target role
8045
     * @param int    $admin    (optional) Flag to revoke only the admin option
8046
     * @param string $type     (optional) Type of revoke: RESTRICT | CASCADE
8047
     *
8048
     * @return \PHPPgAdmin\Database\A 0 success
8049
     */
8050 View Code Duplication
    public function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8051
    {
8052
        $this->fieldClean($role);
8053
        $this->fieldClean($rolename);
8054
8055
        $sql = 'REVOKE ';
8056
        if ($admin == 1) {
8057
            $sql .= 'ADMIN OPTION FOR ';
8058
        }
8059
8060
        $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}";
8061
8062
        return $this->execute($sql);
8063
    }
8064
8065
    /**
8066
     * Removes a role.
8067
     *
8068
     * @param $rolename The name of the role to drop
8069
     *
8070
     * @return \PHPPgAdmin\Database\A 0 success
8071
     */
8072
    public function dropRole($rolename)
8073
    {
8074
        $this->fieldClean($rolename);
8075
8076
        $sql = "DROP ROLE \"{$rolename}\"";
8077
8078
        return $this->execute($sql);
8079
    }
8080
8081
    /**
8082
     * Creates a new user.
8083
     *
8084
     * @param $username   The username of the user to create
8085
     * @param $password   A password for the user
8086
     * @param $createdb   boolean Whether or not the user can create databases
8087
     * @param $createuser boolean Whether or not the user can create other users
8088
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire
8089
     * @param $groups
8090
     *
8091
     * @return \PHPPgAdmin\Database\A 0 success
8092
     *
8093
     * @internal param $group (array) The groups to create the user in
8094
     */
8095
    public function createUser($username, $password, $createdb, $createuser, $expiry, $groups)
8096
    {
8097
        $enc = $this->_encryptPassword($username, $password);
8098
        $this->fieldClean($username);
8099
        $this->clean($enc);
8100
        $this->clean($expiry);
8101
        $this->fieldArrayClean($groups);
8102
8103
        $sql = "CREATE USER \"{$username}\"";
8104
        if ($password != '') {
8105
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
8106
        }
8107
8108
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
8109
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
8110
        if (is_array($groups) && count($groups) > 0) {
8111
            $sql .= ' IN GROUP "'.implode('", "', $groups).'"';
8112
        }
8113
8114
        if ($expiry != '') {
8115
            $sql .= " VALID UNTIL '{$expiry}'";
8116
        } else {
8117
            $sql .= " VALID UNTIL 'infinity'";
8118
        }
8119
8120
        return $this->execute($sql);
8121
    }
8122
8123
    /**
8124
     * Adjusts a user's info and renames the user.
8125
     *
8126
     * @param $username   The username of the user to modify
8127
     * @param $password   A new password for the user
8128
     * @param $createdb   boolean Whether or not the user can create databases
8129
     * @param $createuser boolean Whether or not the user can create other users
8130
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
8131
     * @param $newname    The new name of the user
8132
     *
8133
     * @return bool|int 0 success
8134
     */
8135
    public function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname)
8136
    {
8137
        $status = $this->beginTransaction();
8138
        if ($status != 0) {
8139
            return -1;
8140
        }
8141
8142
        if ($username != $newname) {
8143
            $status = $this->renameUser($username, $newname);
8144
            if ($status != 0) {
8145
                $this->rollbackTransaction();
8146
8147
                return -3;
8148
            }
8149
            $username = $newname;
8150
        }
8151
8152
        $status = $this->setUser($username, $password, $createdb, $createuser, $expiry);
8153
        if ($status != 0) {
8154
            $this->rollbackTransaction();
8155
8156
            return -2;
8157
        }
8158
8159
        return $this->endTransaction();
8160
    }
8161
8162
    /**
8163
     * Renames a user.
8164
     *
8165
     * @param $username The username of the user to rename
8166
     * @param $newname  The new name of the user
8167
     *
8168
     * @return \PHPPgAdmin\Database\A 0 success
8169
     */
8170
    public function renameUser($username, $newname)
8171
    {
8172
        $this->fieldClean($username);
8173
        $this->fieldClean($newname);
8174
8175
        $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\"";
8176
8177
        return $this->execute($sql);
8178
    }
8179
8180
    // Tablespace functions
8181
8182
    /**
8183
     * Adjusts a user's info.
8184
     *
8185
     * @param $username   The username of the user to modify
8186
     * @param $password   A new password for the user
8187
     * @param $createdb   boolean Whether or not the user can create databases
8188
     * @param $createuser boolean Whether or not the user can create other users
8189
     * @param $expiry     string Format 'YYYY-MM-DD HH:MM:SS'.  '' means never expire.
8190
     *
8191
     * @return \PHPPgAdmin\Database\A 0 success
8192
     */
8193
    public function setUser($username, $password, $createdb, $createuser, $expiry)
8194
    {
8195
        $enc = $this->_encryptPassword($username, $password);
8196
        $this->fieldClean($username);
8197
        $this->clean($enc);
8198
        $this->clean($expiry);
8199
8200
        $sql = "ALTER USER \"{$username}\"";
8201
        if ($password != '') {
8202
            $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
8203
        }
8204
8205
        $sql .= $createdb ? ' CREATEDB' : ' NOCREATEDB';
8206
        $sql .= $createuser ? ' CREATEUSER' : ' NOCREATEUSER';
8207
        if ($expiry != '') {
8208
            $sql .= " VALID UNTIL '{$expiry}'";
8209
        } else {
8210
            $sql .= " VALID UNTIL 'infinity'";
8211
        }
8212
8213
        return $this->execute($sql);
8214
    }
8215
8216
    /**
8217
     * Removes a user.
8218
     *
8219
     * @param $username The username of the user to drop
8220
     *
8221
     * @return \PHPPgAdmin\Database\A 0 success
8222
     */
8223
    public function dropUser($username)
8224
    {
8225
        $this->fieldClean($username);
8226
8227
        $sql = "DROP USER \"{$username}\"";
8228
8229
        return $this->execute($sql);
8230
    }
8231
8232
    /**
8233
     * Changes a role's password.
8234
     *
8235
     * @param $rolename The role name
8236
     * @param $password The new password
8237
     *
8238
     * @return \PHPPgAdmin\Database\A 0 success
8239
     */
8240 View Code Duplication
    public function changePassword($rolename, $password)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8241
    {
8242
        $enc = $this->_encryptPassword($rolename, $password);
8243
        $this->fieldClean($rolename);
8244
        $this->clean($enc);
8245
8246
        $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'";
8247
8248
        return $this->execute($sql);
8249
    }
8250
8251
    /**
8252
     * Adds a group member.
8253
     *
8254
     * @param $groname The name of the group
8255
     * @param $user    The name of the user to add to the group
8256
     *
8257
     * @return \PHPPgAdmin\Database\A 0 success
8258
     */
8259
    public function addGroupMember($groname, $user)
8260
    {
8261
        $this->fieldClean($groname);
8262
        $this->fieldClean($user);
8263
8264
        $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
8265
8266
        return $this->execute($sql);
8267
    }
8268
8269
    /**
8270
     * Returns all role names which the role belongs to.
8271
     *
8272
     * @param $rolename The role name
8273
     *
8274
     * @return All role names
8275
     */
8276
    public function getMemberOf($rolename)
8277
    {
8278
        $this->clean($rolename);
8279
8280
        $sql = "
8281
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
8282
			WHERE R.oid=M.roleid
8283
				AND member IN (
8284
					SELECT oid FROM pg_catalog.pg_roles
8285
					WHERE rolname='{$rolename}')
8286
			ORDER BY rolname";
8287
8288
        return $this->selectSet($sql);
8289
    }
8290
8291
    // Administration functions
8292
8293
    /**
8294
     * Returns all role names that are members of a role.
8295
     *
8296
     * @param $rolename The role name
8297
     * @param $admin    (optional) Find only admin members
8298
     *
8299
     * @return All role names
8300
     */
8301
    public function getMembers($rolename, $admin = 'f')
8302
    {
8303
        $this->clean($rolename);
8304
8305
        $sql = "
8306
			SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
8307
			WHERE R.oid=M.member AND admin_option='{$admin}'
8308
				AND roleid IN (SELECT oid FROM pg_catalog.pg_roles
8309
					WHERE rolname='{$rolename}')
8310
			ORDER BY rolname";
8311
8312
        return $this->selectSet($sql);
8313
    }
8314
8315
    /**
8316
     * Removes a group member.
8317
     *
8318
     * @param $groname The name of the group
8319
     * @param $user    The name of the user to remove from the group
8320
     *
8321
     * @return \PHPPgAdmin\Database\A 0 success
8322
     */
8323
    public function dropGroupMember($groname, $user)
8324
    {
8325
        $this->fieldClean($groname);
8326
        $this->fieldClean($user);
8327
8328
        $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
8329
8330
        return $this->execute($sql);
8331
    }
8332
8333
    /**
8334
     * Return users in a specific group.
8335
     *
8336
     * @param $groname The name of the group
8337
     *
8338
     * @return All users in the group
8339
     */
8340
    public function getGroup($groname)
8341
    {
8342
        $this->clean($groname);
8343
8344
        $sql = "
8345
			SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g
8346
			WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist)
8347
			ORDER BY s.usename";
8348
8349
        return $this->selectSet($sql);
8350
    }
8351
8352
    /**
8353
     * Returns all groups in the database cluser.
8354
     *
8355
     * @return All groups
8356
     */
8357
    public function getGroups()
8358
    {
8359
        $sql = 'SELECT groname FROM pg_group ORDER BY groname';
8360
8361
        return $this->selectSet($sql);
8362
    }
8363
8364
    /**
8365
     * Creates a new group.
8366
     *
8367
     * @param $groname The name of the group
8368
     * @param $users   An array of users to add to the group
8369
     *
8370
     * @return \PHPPgAdmin\Database\A 0 success
8371
     */
8372
    public function createGroup($groname, $users)
8373
    {
8374
        $this->fieldClean($groname);
8375
8376
        $sql = "CREATE GROUP \"{$groname}\"";
8377
8378
        if (is_array($users) && count($users) > 0) {
8379
            $this->fieldArrayClean($users);
8380
            $sql .= ' WITH USER "'.implode('", "', $users).'"';
8381
        }
8382
8383
        return $this->execute($sql);
8384
    }
8385
8386
    /**
8387
     * Removes a group.
8388
     *
8389
     * @param $groname The name of the group to drop
8390
     *
8391
     * @return \PHPPgAdmin\Database\A 0 success
8392
     */
8393
    public function dropGroup($groname)
8394
    {
8395
        $this->fieldClean($groname);
8396
8397
        $sql = "DROP GROUP \"{$groname}\"";
8398
8399
        return $this->execute($sql);
8400
    }
8401
8402
    /**
8403
     * Grants a privilege to a user, group or public.
8404
     *
8405
     * @param $mode        'GRANT' or 'REVOKE';
8406
     * @param $type        The type of object
8407
     * @param $object      The name of the object
8408
     * @param $public      True to grant to public, false otherwise
8409
     * @param $usernames   The array of usernames to grant privs to.
8410
     * @param $groupnames  The array of group names to grant privs to.
8411
     * @param $privileges  The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
8412
     * @param $grantoption True if has grant option, false otherwise
8413
     * @param $cascade     True for cascade revoke, false otherwise
8414
     * @param $table       the column's table if type=column
8415
     *
8416
     * @return \PHPPgAdmin\Database\A 0 success
8417
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'GRANT' at position 0 could not be parsed: Unknown type name ''GRANT'' at position 0 in 'GRANT'.
Loading history...
8418
    public function setPrivileges(
8419
        $mode,
8420
        $type,
8421
        $object,
8422
        $public,
8423
        $usernames,
8424
        $groupnames,
8425
        $privileges,
8426
        $grantoption,
8427
        $cascade,
8428
        $table
8429
    ) {
8430
        $f_schema = $this->_schema;
8431
        $this->fieldClean($f_schema);
8432
        $this->fieldArrayClean($usernames);
8433
        $this->fieldArrayClean($groupnames);
8434
8435
        // Input checking
8436
        if (!is_array($privileges) || count($privileges) == 0) {
8437
            return -3;
8438
        }
8439
8440
        if (!is_array($usernames) || !is_array($groupnames) ||
8441
            (!$public && count($usernames) == 0 && count($groupnames) == 0)) {
8442
            return -4;
8443
        }
8444
8445
        if ($mode != 'GRANT' && $mode != 'REVOKE') {
8446
            return -5;
8447
        }
8448
8449
        $sql = $mode;
8450
8451
        // Grant option
8452
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
8453
            $sql .= ' GRANT OPTION FOR';
8454
        }
8455
8456
        if (in_array('ALL PRIVILEGES', $privileges)) {
8457
            $sql .= ' ALL PRIVILEGES';
8458
        } else {
8459
            if ($type == 'column') {
8460
                $this->fieldClean($object);
8461
                $sql .= ' '.implode(" (\"{$object}\"), ", $privileges);
8462
            } else {
8463
                $sql .= ' '.implode(', ', $privileges);
8464
            }
8465
        }
8466
8467
        switch ($type) {
8468
            case 'column':
8469
                $sql .= " (\"{$object}\")";
8470
                $object = $table;
8471
            case 'table':
8472
            case 'view':
8473
            case 'sequence':
8474
                $this->fieldClean($object);
8475
                $sql .= " ON \"{$f_schema}\".\"{$object}\"";
8476
                break;
8477
            case 'database':
8478
                $this->fieldClean($object);
8479
                $sql .= " ON DATABASE \"{$object}\"";
8480
                break;
8481
            case 'function':
8482
                // Function comes in with $object as function OID
8483
                $fn = $this->getFunction($object);
8484
                $this->fieldClean($fn->fields['proname']);
8485
                $sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
8486
                break;
8487
            case 'language':
8488
                $this->fieldClean($object);
8489
                $sql .= " ON LANGUAGE \"{$object}\"";
8490
                break;
8491
            case 'schema':
8492
                $this->fieldClean($object);
8493
                $sql .= " ON SCHEMA \"{$object}\"";
8494
                break;
8495
            case 'tablespace':
8496
                $this->fieldClean($object);
8497
                $sql .= " ON TABLESPACE \"{$object}\"";
8498
                break;
8499
            default:
8500
                return -1;
8501
        }
8502
8503
        // Dump PUBLIC
8504
        $first = true;
8505
        $sql .= ($mode == 'GRANT') ? ' TO ' : ' FROM ';
8506
        if ($public) {
8507
            $sql .= 'PUBLIC';
8508
            $first = false;
8509
        }
8510
        // Dump users
8511
        foreach ($usernames as $v) {
8512
            if ($first) {
8513
                $sql .= "\"{$v}\"";
8514
                $first = false;
8515
            } else {
8516
                $sql .= ", \"{$v}\"";
8517
            }
8518
        }
8519
        // Dump groups
8520
        foreach ($groupnames as $v) {
8521
            if ($first) {
8522
                $sql .= "GROUP \"{$v}\"";
8523
                $first = false;
8524
            } else {
8525
                $sql .= ", GROUP \"{$v}\"";
8526
            }
8527
        }
8528
8529
        // Grant option
8530
        if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
8531
            $sql .= ' WITH GRANT OPTION';
8532
        }
8533
8534
        // Cascade revoke
8535
        if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
8536
            $sql .= ' CASCADE';
8537
        }
8538
8539
        return $this->execute($sql);
8540
    }
8541
8542
    /**
8543
     * Retrieves information for all tablespaces.
8544
     *
8545
     * @param bool|\PHPPgAdmin\Database\Include $all Include all tablespaces (necessary when moving objects back to the default space)
8546
     *
8547
     * @return \PHPPgAdmin\Database\A recordset
8548
     */
8549
    public function getTablespaces($all = false)
8550
    {
8551
        $conf = $this->conf;
8552
8553
        $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation,
8554
                    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment
8555
					FROM pg_catalog.pg_tablespace";
8556
8557
        if (!$conf['show_system'] && !$all) {
8558
            $sql .= ' WHERE spcname NOT LIKE $$pg\_%$$';
8559
        }
8560
8561
        $sql .= ' ORDER BY spcname';
8562
8563
        return $this->selectSet($sql);
8564
    }
8565
8566
    // Misc functions
8567
8568
    /**
8569
     * Retrieves a tablespace's information.
8570
     *
8571
     * @param $spcname
8572
     *
8573
     * @return \PHPPgAdmin\Database\A recordset
8574
     */
8575
    public function getTablespace($spcname)
8576
    {
8577
        $this->clean($spcname);
8578
8579
        $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, pg_catalog.pg_tablespace_location(oid) as spclocation,
8580
                    (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid AND pd.classoid='pg_tablespace'::regclass) AS spccomment
8581
					FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
8582
8583
        return $this->selectSet($sql);
8584
    }
8585
8586
    /**
8587
     * Creates a tablespace.
8588
     *
8589
     * @param        $spcname  The name of the tablespace to create
8590
     * @param        $spcowner The owner of the tablespace. '' for current
8591
     * @param        $spcloc   The directory in which to create the tablespace
8592
     * @param string $comment
8593
     *
8594
     * @return int 0 success
8595
     */
8596
    public function createTablespace($spcname, $spcowner, $spcloc, $comment = '')
8597
    {
8598
        $this->fieldClean($spcname);
8599
        $this->clean($spcloc);
8600
8601
        $sql = "CREATE TABLESPACE \"{$spcname}\"";
8602
8603
        if ($spcowner != '') {
8604
            $this->fieldClean($spcowner);
8605
            $sql .= " OWNER \"{$spcowner}\"";
8606
        }
8607
8608
        $sql .= " LOCATION '{$spcloc}'";
8609
8610
        $status = $this->execute($sql);
8611
        if ($status != 0) {
8612
            return -1;
8613
        }
8614
8615 View Code Duplication
        if ($comment != '' && $this->hasSharedComments()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8616
            $status = $this->setComment('TABLESPACE', $spcname, '', $comment);
8617
            if ($status != 0) {
8618
                return -2;
8619
            }
8620
        }
8621
8622
        return 0;
8623
    }
8624
8625
    /**
8626
     * Alters a tablespace.
8627
     *
8628
     * @param        $spcname The name of the tablespace
8629
     * @param        $name    The new name for the tablespace
8630
     * @param        $owner   The new owner for the tablespace
8631
     * @param string $comment
8632
     *
8633
     * @return bool|int 0 success
8634
     */
8635
    public function alterTablespace($spcname, $name, $owner, $comment = '')
8636
    {
8637
        $this->fieldClean($spcname);
8638
        $this->fieldClean($name);
8639
        $this->fieldClean($owner);
8640
8641
        // Begin transaction
8642
        $status = $this->beginTransaction();
8643
        if ($status != 0) {
8644
            return -1;
8645
        }
8646
8647
        // Owner
8648
        $sql = "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\"";
8649
        $status = $this->execute($sql);
8650
        if ($status != 0) {
8651
            $this->rollbackTransaction();
8652
8653
            return -2;
8654
        }
8655
8656
        // Rename (only if name has changed)
8657
        if ($name != $spcname) {
8658
            $sql = "ALTER TABLESPACE \"{$spcname}\" RENAME TO \"{$name}\"";
8659
            $status = $this->execute($sql);
8660
            if ($status != 0) {
8661
                $this->rollbackTransaction();
8662
8663
                return -3;
8664
            }
8665
8666
            $spcname = $name;
8667
        }
8668
8669
        // Set comment if it has changed
8670 View Code Duplication
        if (trim($comment) != '' && $this->hasSharedComments()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8671
            $status = $this->setComment('TABLESPACE', $spcname, '', $comment);
8672
            if ($status != 0) {
8673
                return -4;
8674
            }
8675
        }
8676
8677
        return $this->endTransaction();
8678
    }
8679
8680
    /**
8681
     * Drops a tablespace.
8682
     *
8683
     * @param $spcname The name of the domain to drop
8684
     *
8685
     * @return \PHPPgAdmin\Database\A 0 success
8686
     */
8687
    public function dropTablespace($spcname)
8688
    {
8689
        $this->fieldClean($spcname);
8690
8691
        $sql = "DROP TABLESPACE \"{$spcname}\"";
8692
8693
        return $this->execute($sql);
8694
    }
8695
8696
    /**
8697
     * Analyze a database.
8698
     *
8699
     * @param $table (optional) The table to analyze
8700
     *
8701
     * @return \PHPPgAdmin\Database\A
8702
     */
8703 View Code Duplication
    public function analyzeDB($table = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8704
    {
8705
        if ($table != '') {
8706
            $f_schema = $this->_schema;
8707
            $this->fieldClean($f_schema);
8708
            $this->fieldClean($table);
8709
8710
            $sql = "ANALYZE \"{$f_schema}\".\"{$table}\"";
8711
        } else {
8712
            $sql = 'ANALYZE';
8713
        }
8714
8715
        return $this->execute($sql);
8716
    }
8717
8718
    /**
8719
     * Vacuums a database.
8720
     *
8721
     * @param \PHPPgAdmin\Database\The|string $table   The table to vacuum
8722
     * @param bool|\PHPPgAdmin\Database\If    $analyze If true, also does analyze
8723
     * @param bool|\PHPPgAdmin\Database\If    $full    If true, selects "full" vacuum
8724
     * @param bool|\PHPPgAdmin\Database\If    $freeze  If true, selects aggressive "freezing" of tuples
8725
     *
8726
     * @return \PHPPgAdmin\Database\A
8727
     */
8728
    public function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false)
8729
    {
8730
        $sql = 'VACUUM';
8731
        if ($full) {
8732
            $sql .= ' FULL';
8733
        }
8734
8735
        if ($freeze) {
8736
            $sql .= ' FREEZE';
8737
        }
8738
8739
        if ($analyze) {
8740
            $sql .= ' ANALYZE';
8741
        }
8742
8743
        if ($table != '') {
8744
            $f_schema = $this->_schema;
8745
            $this->fieldClean($f_schema);
8746
            $this->fieldClean($table);
8747
            $sql .= " \"{$f_schema}\".\"{$table}\"";
8748
        }
8749
8750
        return $this->execute($sql);
8751
    }
8752
8753
    /**
8754
     * Returns all autovacuum global configuration.
8755
     *
8756
     * @return associative array array( param => value, ...)
8757
     */
8758
    public function getAutovacuum()
8759
    {
8760
        $_defaults = $this->selectSet("SELECT name, setting
8761
			FROM pg_catalog.pg_settings
8762
			WHERE
8763
				name = 'autovacuum'
8764
				OR name = 'autovacuum_vacuum_threshold'
8765
				OR name = 'autovacuum_vacuum_scale_factor'
8766
				OR name = 'autovacuum_analyze_threshold'
8767
				OR name = 'autovacuum_analyze_scale_factor'
8768
				OR name = 'autovacuum_vacuum_cost_delay'
8769
				OR name = 'autovacuum_vacuum_cost_limit'
8770
				OR name = 'vacuum_freeze_min_age'
8771
				OR name = 'autovacuum_freeze_max_age'
8772
			"
8773
        );
8774
8775
        $ret = [];
8776
        while (!$_defaults->EOF) {
8777
            $ret[$_defaults->fields['name']] = $_defaults->fields['setting'];
8778
            $_defaults->moveNext();
8779
        }
8780
8781
        return $ret;
8782
    }
8783
8784
    /**
8785
     * Returns all available autovacuum per table information.
8786
     *
8787
     * @param $table
8788
     * @param $vacenabled
8789
     * @param $vacthreshold
8790
     * @param $vacscalefactor
8791
     * @param $anathresold
8792
     * @param $anascalefactor
8793
     * @param $vaccostdelay
8794
     * @param $vaccostlimit
8795
     *
8796
     * @return \PHPPgAdmin\Database\A recordset
8797
     */
8798
    public function saveAutovacuum(
8799
        $table,
8800
        $vacenabled,
8801
        $vacthreshold,
8802
        $vacscalefactor,
8803
        $anathresold,
8804
        $anascalefactor,
8805
        $vaccostdelay,
8806
        $vaccostlimit
8807
    ) {
8808
        $f_schema = $this->_schema;
8809
        $this->fieldClean($f_schema);
8810
        $this->fieldClean($table);
8811
8812
        $sql = "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET (";
8813
8814
        if (!empty($vacenabled)) {
8815
            $this->clean($vacenabled);
8816
            $params[] = "autovacuum_enabled='{$vacenabled}'";
8817
        }
8818
        if (!empty($vacthreshold)) {
8819
            $this->clean($vacthreshold);
8820
            $params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'";
8821
        }
8822
        if (!empty($vacscalefactor)) {
8823
            $this->clean($vacscalefactor);
8824
            $params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'";
8825
        }
8826
        if (!empty($anathresold)) {
8827
            $this->clean($anathresold);
8828
            $params[] = "autovacuum_analyze_threshold='{$anathresold}'";
8829
        }
8830
        if (!empty($anascalefactor)) {
8831
            $this->clean($anascalefactor);
8832
            $params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'";
8833
        }
8834
        if (!empty($vaccostdelay)) {
8835
            $this->clean($vaccostdelay);
8836
            $params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'";
8837
        }
8838
        if (!empty($vaccostlimit)) {
8839
            $this->clean($vaccostlimit);
8840
            $params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'";
8841
        }
8842
8843
        $sql = $sql.implode(',', $params).');';
8844
8845
        return $this->execute($sql);
8846
    }
8847
8848
    // Type conversion routines
8849
8850
    public function dropAutovacuum($table)
8851
    {
8852
        $f_schema = $this->_schema;
8853
        $this->fieldClean($f_schema);
8854
        $this->fieldClean($table);
8855
8856
        return $this->execute("
8857
			ALTER TABLE \"{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold,
8858
				autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor,
8859
				autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit
8860
			);"
8861
        );
8862
    }
8863
8864
    /**
8865
     * Returns all available process information.
8866
     *
8867
     * @param $database (optional) Find only connections to specified database
8868
     *
8869
     * @return A recordset
8870
     */
8871
    public function getProcesses($database = null)
8872
    {
8873
        if ($database === null) {
8874
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
8875
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
8876
				FROM pg_catalog.pg_stat_activity
8877
				ORDER BY datname, usename, pid";
8878
        } else {
8879
            $this->clean($database);
8880
            $sql = "SELECT datname, usename, pid, waiting, state_change as query_start,
8881
                  case when state='idle in transaction' then '<IDLE> in transaction' when state = 'idle' then '<IDLE>' else query end as query
8882
				FROM pg_catalog.pg_stat_activity
8883
				WHERE datname='{$database}'
8884
				ORDER BY usename, pid";
8885
        }
8886
8887
        return $this->selectSet($sql);
8888
    }
8889
8890
    // interfaces Statistics collector functions
8891
8892
    /**
8893
     * Returns table locks information in the current database.
8894
     *
8895
     * @return A recordset
8896
     */
8897 View Code Duplication
    public function getLocks()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
8898
    {
8899
        $conf = $this->conf;
8900
8901
        if (!$conf['show_system']) {
8902
            $where = 'AND pn.nspname NOT LIKE $$pg\_%$$';
8903
        } else {
8904
            $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
8905
        }
8906
8907
        $sql = "
8908
			SELECT
8909
				pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction,
8910
				(select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid'
8911
					and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction
8912
			FROM
8913
				pg_catalog.pg_locks pl,
8914
				pg_catalog.pg_class pc,
8915
				pg_catalog.pg_namespace pn
8916
			WHERE
8917
				pl.relation = pc.oid AND pc.relnamespace=pn.oid
8918
			{$where}
8919
			ORDER BY pid,nspname,tablename";
8920
8921
        return $this->selectSet($sql);
8922
    }
8923
8924
    /**
8925
     * Sends a cancel or kill command to a process.
8926
     *
8927
     * @param $pid    The ID of the backend process
8928
     * @param $signal 'CANCEL'
8929
     *
8930
     * @return int 0 success
8931
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 'CANCEL' at position 0 could not be parsed: Unknown type name ''CANCEL'' at position 0 in 'CANCEL'.
Loading history...
8932
    public function sendSignal($pid, $signal)
8933
    {
8934
        // Clean
8935
        $pid = (int) $pid;
8936
8937
        if ($signal == 'CANCEL') {
8938
            $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
8939
        } elseif ($signal == 'KILL') {
8940
            $sql = "SELECT pg_catalog.pg_terminate_backend({$pid}) AS val";
8941
        } else {
8942
            return -1;
8943
        }
8944
8945
        // Execute the query
8946
        $val = $this->selectField($sql, 'val');
8947
8948
        if ($val === 'f') {
8949
            return -1;
8950
        }
8951
8952
        if ($val === 't') {
8953
            return 0;
8954
        } else {
8955
            return -1;
8956
        }
8957
    }
8958
8959
    /**
8960
     * Executes an SQL script as a series of SQL statements.  Returns
8961
     * the result of the final step.  This is a very complicated lexer
8962
     * based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
8963
     * the PostgreSQL source code.
8964
     * XXX: It does not handle multibyte languages properly.
8965
     *
8966
     * @param $name     Entry in $_FILES to use
8967
     * @param $callback (optional) Callback function to call with each query,
8968
     *                  its result and line number.
8969
     *
8970
     * @return true for general success, false on any failure.
8971
     */
8972
    public function executeScript($name, $callback = null)
8973
    {
8974
8975
        // This whole function isn't very encapsulated, but hey...
8976
        $conn = $this->conn->_connectionID;
8977
        if (!is_uploaded_file($_FILES[$name]['tmp_name'])) {
8978
            return false;
8979
        }
8980
8981
        $fd = fopen($_FILES[$name]['tmp_name'], 'rb');
8982
        if (!$fd) {
8983
            return false;
8984
        }
8985
8986
        // Build up each SQL statement, they can be multiline
8987
        $query_buf = null;
8988
        $query_start = 0;
8989
        $in_quote = 0;
8990
        $in_xcomment = 0;
8991
        $bslash_count = 0;
8992
        $dol_quote = null;
8993
        $paren_level = 0;
8994
        $len = 0;
8995
        $i = 0;
8996
        $prevlen = 0;
8997
        $thislen = 0;
8998
        $lineno = 0;
8999
9000
        // Loop over each line in the file
9001
        while (!feof($fd)) {
9002
            $line = fgets($fd);
9003
            $lineno++;
9004
9005
            // Nothing left on line? Then ignore...
9006
            if (trim($line) == '') {
9007
                continue;
9008
            }
9009
9010
            $len = strlen($line);
9011
            $query_start = 0;
9012
9013
            /*
9014
             * Parse line, looking for command separators.
9015
             *
9016
             * The current character is at line[i], the prior character at line[i
9017
             * - prevlen], the next character at line[i + thislen].
9018
             */
9019
            $prevlen = 0;
9020
            $thislen = ($len > 0) ? 1 : 0;
9021
9022
            for ($i = 0; $i < $len; $this->advance_1($i, $prevlen, $thislen)) {
9023
9024
                /* was the previous character a backslash? */
9025
                if ($i > 0 && substr($line, $i - $prevlen, 1) == '\\') {
9026
                    $bslash_count++;
9027
                } else {
9028
                    $bslash_count = 0;
9029
                }
9030
9031
                /*
9032
                 * It is important to place the in_* test routines before the
9033
                 * in_* detection routines. i.e. we have to test if we are in
9034
                 * a quote before testing for comments.
9035
                 */
9036
9037
                /* in quote? */
9038
                if ($in_quote !== 0) {
9039
                    /*
9040
                     * end of quote if matching non-backslashed character.
9041
                     * backslashes don't count for double quotes, though.
9042
                     */
9043
                    if (substr($line, $i, 1) == $in_quote &&
9044
                        ($bslash_count % 2 == 0 || $in_quote == '"')) {
9045
                        $in_quote = 0;
9046
                    }
9047
                } /* in or end of $foo$ type quote? */
9048
                else {
9049
                    if ($dol_quote) {
9050
                        if (strncmp(substr($line, $i), $dol_quote, strlen($dol_quote)) == 0) {
9051
                            $this->advance_1($i, $prevlen, $thislen);
9052
                            while (substr($line, $i, 1) != '$') {
9053
                                $this->advance_1($i, $prevlen, $thislen);
9054
                            }
9055
9056
                            $dol_quote = null;
9057
                        }
9058
                    } /* start of extended comment? */
9059
                    else {
9060
                        if (substr($line, $i, 2) == '/*') {
9061
                            $in_xcomment++;
9062
                            if ($in_xcomment == 1) {
9063
                                $this->advance_1($i, $prevlen, $thislen);
9064
                            }
9065
                        } /* in or end of extended comment? */
9066
                        else {
9067
                            if ($in_xcomment) {
9068
                                if (substr($line, $i, 2) == '*/' && !--$in_xcomment) {
9069
                                    $this->advance_1($i, $prevlen, $thislen);
9070
                                }
9071
                            } /* start of quote? */
9072
                            else {
9073
                                if (substr($line, $i, 1) == '\'' || substr($line, $i, 1) == '"') {
9074
                                    $in_quote = substr($line, $i, 1);
9075
                                } /*
9076
                                 * start of $foo$ type quote?
9077
                                 */
9078
                                else {
9079
                                    if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
9080
                                        $dol_end = strpos(substr($line, $i + 1), '$');
9081
                                        $dol_quote = substr($line, $i, $dol_end + 1);
9082
                                        $this->advance_1($i, $prevlen, $thislen);
9083
                                        while (substr($line, $i, 1) != '$') {
9084
                                            $this->advance_1($i, $prevlen, $thislen);
9085
                                        }
9086
                                    } /* single-line comment? truncate line */
9087
                                    else {
9088
                                        if (substr($line, $i, 2) == '--') {
9089
                                            $line = substr($line, 0, $i); /* remove comment */
9090
                                            break;
9091
                                        } /* count nested parentheses */
9092
9093
                                        if (substr($line, $i, 1) == '(') {
9094
                                            $paren_level++;
9095
                                        } else {
9096
                                            if (substr($line, $i, 1) == ')' && $paren_level > 0) {
9097
                                                $paren_level--;
9098
                                            } /* semicolon? then send query */
9099
                                            else {
9100
                                                if (substr($line, $i, 1) == ';' && !$bslash_count && !$paren_level) {
9101
                                                    $subline = substr(substr($line, 0, $i), $query_start);
9102
                                                    /*
9103
                                                     * insert a cosmetic newline, if this is not the first
9104
                                                     * line in the buffer
9105
                                                     */
9106
                                                    if (strlen($query_buf) > 0) {
9107
                                                        $query_buf .= "\n";
9108
                                                    }
9109
9110
                                                    /* append the line to the query buffer */
9111
                                                    $query_buf .= $subline;
9112
                                                    /* is there anything in the query_buf? */
9113
                                                    if (trim($query_buf)) {
9114
                                                        $query_buf .= ';';
9115
9116
                                                        // Execute the query. PHP cannot execute
9117
                                                        // empty queries, unlike libpq
9118
                                                        $res = @pg_query($conn, $query_buf);
9119
9120
                                                        // Call the callback function for display
9121
                                                        if ($callback !== null) {
9122
                                                            $callback($query_buf, $res, $lineno);
9123
                                                        }
9124
9125
                                                        // Check for COPY request
9126 View Code Duplication
                                                        if (pg_result_status($res) == 4) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9127
                                                            // 4 == PGSQL_COPY_FROM
9128
                                                            while (!feof($fd)) {
9129
                                                                $copy = fgets($fd, 32768);
9130
                                                                $lineno++;
9131
                                                                pg_put_line($conn, $copy);
9132
                                                                if ($copy == "\\.\n" || $copy == "\\.\r\n") {
9133
                                                                    pg_end_copy($conn);
9134
                                                                    break;
9135
                                                                }
9136
                                                            }
9137
                                                        }
9138
                                                    }
9139
                                                    $query_buf = null;
9140
                                                    $query_start = $i + $thislen;
9141
                                                }
9142
9143
                                                /*
9144
                                                 * keyword or identifier?
9145
                                                 * We grab the whole string so that we don't
9146
                                                 * mistakenly see $foo$ inside an identifier as the start
9147
                                                 * of a dollar quote.
9148
                                                 */
9149
                                                // XXX: multibyte here
9150
                                                else {
9151
                                                    if (preg_match('/^[_[:alpha:]]$/', substr($line, $i, 1))) {
9152
                                                        $sub = substr($line, $i, $thislen);
9153
                                                        while (preg_match('/^[\$_A-Za-z0-9]$/', $sub)) {
9154
                                                            /* keep going while we still have identifier chars */
9155
                                                            $this->advance_1($i, $prevlen, $thislen);
9156
                                                            $sub = substr($line, $i, $thislen);
9157
                                                        }
9158
                                                        // Since we're now over the next character to be examined, it is necessary
9159
                                                        // to move back one space.
9160
                                                        $i -= $prevlen;
9161
                                                    }
9162
                                                }
9163
                                            }
9164
                                        }
9165
                                    }
9166
                                }
9167
                            }
9168
                        }
9169
                    }
9170
                }
9171
            } // end for
9172
9173
            /* Put the rest of the line in the query buffer. */
9174
            $subline = substr($line, $query_start);
9175
            if ($in_quote || $dol_quote || strspn($subline, " \t\n\r") != strlen($subline)) {
9176
                if (strlen($query_buf) > 0) {
9177
                    $query_buf .= "\n";
9178
                }
9179
9180
                $query_buf .= $subline;
9181
            }
9182
9183
            $line = null;
9184
        } // end while
9185
9186
        /*
9187
         * Process query at the end of file without a semicolon, so long as
9188
         * it's non-empty.
9189
         */
9190
        if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf)) {
9191
            // Execute the query
9192
            $res = @pg_query($conn, $query_buf);
9193
9194
            // Call the callback function for display
9195
            if ($callback !== null) {
9196
                $callback($query_buf, $res, $lineno);
9197
            }
9198
9199
            // Check for COPY request
9200 View Code Duplication
            if (pg_result_status($res) == 4) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9201
                // 4 == PGSQL_COPY_FROM
9202
                while (!feof($fd)) {
9203
                    $copy = fgets($fd, 32768);
9204
                    $lineno++;
9205
                    pg_put_line($conn, $copy);
9206
                    if ($copy == "\\.\n" || $copy == "\\.\r\n") {
9207
                        pg_end_copy($conn);
9208
                        break;
9209
                    }
9210
                }
9211
            }
9212
        }
9213
9214
        fclose($fd);
9215
9216
        return true;
9217
    }
9218
9219
    /**
9220
     * A private helper method for executeScript that advances the
9221
     * character by 1.  In psql this is careful to take into account
9222
     * multibyte languages, but we don't at the moment, so this function
9223
     * is someone redundant, since it will always advance by 1.
9224
     *
9225
     * @param &$i       The current character position in the line
9226
     * @param &$prevlen Length of previous character (ie. 1)
9227
     * @param &$thislen Length of current character (ie. 1)
9228
     */
9229
    private function advance_1(&$i, &$prevlen, &$thislen)
9230
    {
9231
        $prevlen = $thislen;
9232
        $i += $thislen;
9233
        $thislen = 1;
9234
    }
9235
9236
    /**
9237
     * Private helper method to detect a valid $foo$ quote delimiter at
9238
     * the start of the parameter dquote.
9239
     *
9240
     * @param $dquote
9241
     *
9242
     * @return true if valid, false otherwise
9243
     */
9244
    private function valid_dolquote($dquote)
9245
    {
9246
        // XXX: support multibyte
9247
        return preg_match('/^[$][$]/', $dquote) || preg_match('/^[$][_[:alpha:]][_[:alnum:]]*[$]/', $dquote);
9248
    }
9249
9250
    // Capabilities
9251
9252
    /**
9253
     * Returns a recordset of all columns in a query.  Supports paging.
9254
     *
9255
     * @param $type       Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
9256
     *                    or 'SELECT" if it's a select query
9257
     * @param $table      The base table of the query.  NULL for no table.
9258
     * @param $query      The query that is being executed.  NULL for no query.
9259
     * @param $sortkey    The column number to sort by, or '' or null for no sorting
9260
     * @param $sortdir    The direction in which to sort the specified column ('asc' or 'desc')
9261
     * @param $page       The page of the relation to retrieve
9262
     * @param $page_size  The number of rows per page
9263
     * @param &$max_pages (return-by-ref) The max number of pages in the relation
9264
     *
9265
     * @return A  recordset on success
9266
     * @return -1 transaction error
9267
     * @return -2 counting error
9268
     * @return -3 page or page_size invalid
9269
     * @return -4 unknown type
9270
     * @return -5 failed setting transaction read only
9271
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment (return-by-ref) at position 1 could not be parsed: Unknown type name 'return-by-ref' at position 1 in (return-by-ref).
Loading history...
9272
    public function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages)
9273
    {
9274
        // Check that we're not going to divide by zero
9275
        if (!is_numeric($page_size) || $page_size != (int) $page_size || $page_size <= 0) {
9276
            return -3;
9277
        }
9278
9279
        // If $type is TABLE, then generate the query
9280
        switch ($type) {
9281
            case 'TABLE':
9282
                if (preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
9283
                    $orderby = [$sortkey => $sortdir];
9284
                } else {
9285
                    $orderby = [];
9286
                }
9287
9288
                $query = $this->getSelectSQL($table, [], [], [], $orderby);
9289
                break;
9290
            case 'QUERY':
9291
            case 'SELECT':
9292
                // Trim query
9293
                $query = trim($query);
9294
                // Trim off trailing semi-colon if there is one
9295
                if (substr($query, strlen($query) - 1, 1) == ';') {
9296
                    $query = substr($query, 0, strlen($query) - 1);
9297
                }
9298
9299
                break;
9300
            default:
9301
                return -4;
9302
        }
9303
9304
        // Generate count query
9305
        $count = "SELECT COUNT(*) AS total FROM ($query) AS sub";
9306
9307
        // Open a transaction
9308
        $status = $this->beginTransaction();
9309
        if ($status != 0) {
9310
            return -1;
9311
        }
9312
9313
        // If backend supports read only queries, then specify read only mode
9314
        // to avoid side effects from repeating queries that do writes.
9315
        if ($this->hasReadOnlyQueries()) {
9316
            $status = $this->execute('SET TRANSACTION READ ONLY');
9317
            if ($status != 0) {
9318
                $this->rollbackTransaction();
9319
9320
                return -5;
9321
            }
9322
        }
9323
9324
        // Count the number of rows
9325
        $total = $this->browseQueryCount($query, $count);
9326
        if ($total < 0) {
9327
            $this->rollbackTransaction();
9328
9329
            return -2;
9330
        }
9331
9332
        // Calculate max pages
9333
        $max_pages = ceil($total / $page_size);
9334
9335
        // Check that page is less than or equal to max pages
9336
        if (!is_numeric($page) || $page != (int) $page || $page > $max_pages || $page < 1) {
9337
            $this->rollbackTransaction();
9338
9339
            return -3;
9340
        }
9341
9342
        // Set fetch mode to NUM so that duplicate field names are properly returned
9343
        // for non-table queries.  Since the SELECT feature only allows selecting one
9344
        // table, duplicate fields shouldn't appear.
9345
        if ($type == 'QUERY') {
9346
            $this->conn->setFetchMode(ADODB_FETCH_NUM);
9347
        }
9348
9349
        // Figure out ORDER BY.  Sort key is always the column number (based from one)
9350
        // of the column to order by.  Only need to do this for non-TABLE queries
9351
        if ($type != 'TABLE' && preg_match('/^[0-9]+$/', $sortkey) && $sortkey > 0) {
9352
            $orderby = " ORDER BY {$sortkey}";
9353
            // Add sort order
9354
            if ($sortdir == 'desc') {
9355
                $orderby .= ' DESC';
9356
            } else {
9357
                $orderby .= ' ASC';
9358
            }
9359
        } else {
9360
            $orderby = '';
9361
        }
9362
9363
        // Actually retrieve the rows, with offset and limit
9364
        $rs = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET ".($page - 1) * $page_size);
9365
        $status = $this->endTransaction();
9366
        if ($status != 0) {
9367
            $this->rollbackTransaction();
9368
9369
            return -1;
9370
        }
9371
9372
        return $rs;
9373
    }
9374
9375
    /**
9376
     * Generates the SQL for the 'select' function.
9377
     *
9378
     * @param $table   The table from which to select
9379
     * @param $show    An array of columns to show.  Empty array means all columns.
9380
     * @param $values  An array mapping columns to values
9381
     * @param $ops     An array of the operators to use
9382
     * @param $orderby (optional) An array of column numbers or names (one based)
9383
     *                 mapped to sort direction (asc or desc or '' or null) to order by
9384
     *
9385
     * @return The SQL query
9386
     */
9387
    public function getSelectSQL($table, $show, $values, $ops, $orderby = [])
9388
    {
9389
        $this->fieldArrayClean($show);
9390
9391
        // If an empty array is passed in, then show all columns
9392
        if (count($show) == 0) {
9393
            if ($this->hasObjectID($table)) {
9394
                $sql = "SELECT \"{$this->id}\", * FROM ";
9395
            } else {
9396
                $sql = 'SELECT * FROM ';
9397
            }
9398
        } else {
9399
            // Add oid column automatically to results for editing purposes
9400
            if (!in_array($this->id, $show) && $this->hasObjectID($table)) {
9401
                $sql = "SELECT \"{$this->id}\", \"";
9402
            } else {
9403
                $sql = 'SELECT "';
9404
            }
9405
9406
            $sql .= implode('","', $show).'" FROM ';
9407
        }
9408
9409
        $this->fieldClean($table);
9410
9411
        if (isset($_REQUEST['schema'])) {
9412
            $f_schema = $_REQUEST['schema'];
9413
            $this->fieldClean($f_schema);
9414
            $sql .= "\"{$f_schema}\".";
9415
        }
9416
        $sql .= "\"{$table}\"";
9417
9418
        // If we have values specified, add them to the WHERE clause
9419
        $first = true;
9420
        if (is_array($values) && count($values) > 0) {
9421
            foreach ($values as $k => $v) {
9422
                if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
9423
                    $this->fieldClean($k);
9424
                    if ($first) {
9425
                        $sql .= ' WHERE ';
9426
                        $first = false;
9427
                    } else {
9428
                        $sql .= ' AND ';
9429
                    }
9430
                    // Different query format depending on operator type
9431
                    switch ($this->selectOps[$ops[$k]]) {
9432
                        case 'i':
9433
                            // Only clean the field for the inline case
9434
                            // this is because (x), subqueries need to
9435
                            // to allow 'a','b' as input.
9436
                            $this->clean($v);
9437
                            $sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
9438
                            break;
9439
                        case 'p':
9440
                            $sql .= "\"{$k}\" {$ops[$k]}";
9441
                            break;
9442
                        case 'x':
9443
                            $sql .= "\"{$k}\" {$ops[$k]} ({$v})";
9444
                            break;
9445
                        case 't':
9446
                            $sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
9447
                            break;
9448
                        default:
9449
                            // Shouldn't happen
9450
                    }
9451
                }
9452
            }
9453
        }
9454
9455
        // ORDER BY
9456
        if (is_array($orderby) && count($orderby) > 0) {
9457
            $sql .= ' ORDER BY ';
9458
            $first = true;
9459
            foreach ($orderby as $k => $v) {
9460
                if ($first) {
9461
                    $first = false;
9462
                } else {
9463
                    $sql .= ', ';
9464
                }
9465
9466
                if (preg_match('/^[0-9]+$/', $k)) {
9467
                    $sql .= $k;
9468
                } else {
9469
                    $this->fieldClean($k);
9470
                    $sql .= '"'.$k.'"';
9471
                }
9472
                if (strtoupper($v) == 'DESC') {
9473
                    $sql .= ' DESC';
9474
                }
9475
            }
9476
        }
9477
9478
        return $sql;
9479
    }
9480
9481
    public function hasReadOnlyQueries()
9482
    {
9483
        return true;
9484
    }
9485
9486
    /**
9487
     * Finds the number of rows that would be returned by a
9488
     * query.
9489
     *
9490
     * @param $query The SQL query
9491
     * @param $count The count query
9492
     *
9493
     * @return The count of rows
9494
     * @return -1  error
9495
     */
9496
    public function browseQueryCount($query, $count)
9497
    {
9498
        return $this->selectField($count, 'total');
9499
    }
9500
9501
    /**
9502
     * Returns a recordset of all columns in a table.
9503
     *
9504
     * @param $table The name of a table
9505
     * @param $key   The associative array holding the key to retrieve
9506
     *
9507
     * @return A recordset
9508
     */
9509
    public function browseRow($table, $key)
9510
    {
9511
        $f_schema = $this->_schema;
9512
        $this->fieldClean($f_schema);
9513
        $this->fieldClean($table);
9514
9515
        $sql = "SELECT * FROM \"{$f_schema}\".\"{$table}\"";
9516
        if (is_array($key) && count($key) > 0) {
9517
            $sql .= ' WHERE true';
9518
            foreach ($key as $k => $v) {
9519
                $this->fieldClean($k);
9520
                $this->clean($v);
9521
                $sql .= " AND \"{$k}\"='{$v}'";
9522
            }
9523
        }
9524
9525
        return $this->selectSet($sql);
9526
    }
9527
9528
    /**
9529
     * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false.
9530
     *
9531
     * @param $parameter the parameter
9532
     *
9533
     * @return \PHPPgAdmin\Database\the|string
9534
     */
9535
    public function dbBool(&$parameter)
9536
    {
9537
        if ($parameter) {
9538
            $parameter = 't';
9539
        } else {
9540
            $parameter = 'f';
9541
        }
9542
9543
        return $parameter;
9544
    }
9545
9546
    /**
9547
     * Fetches statistics for a database.
9548
     *
9549
     * @param $database The database to fetch stats for
9550
     *
9551
     * @return A recordset
9552
     */
9553
    public function getStatsDatabase($database)
9554
    {
9555
        $this->clean($database);
9556
9557
        $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'";
9558
9559
        return $this->selectSet($sql);
9560
    }
9561
9562
    /**
9563
     * Fetches tuple statistics for a table.
9564
     *
9565
     * @param $table The table to fetch stats for
9566
     *
9567
     * @return A recordset
9568
     */
9569 View Code Duplication
    public function getStatsTableTuples($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9570
    {
9571
        $c_schema = $this->_schema;
9572
        $this->clean($c_schema);
9573
        $this->clean($table);
9574
9575
        $sql = "SELECT * FROM pg_stat_all_tables
9576
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
9577
9578
        return $this->selectSet($sql);
9579
    }
9580
9581
    /**
9582
     * Fetches I/0 statistics for a table.
9583
     *
9584
     * @param $table The table to fetch stats for
9585
     *
9586
     * @return A recordset
9587
     */
9588 View Code Duplication
    public function getStatsTableIO($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9589
    {
9590
        $c_schema = $this->_schema;
9591
        $this->clean($c_schema);
9592
        $this->clean($table);
9593
9594
        $sql = "SELECT * FROM pg_statio_all_tables
9595
			WHERE schemaname='{$c_schema}' AND relname='{$table}'";
9596
9597
        return $this->selectSet($sql);
9598
    }
9599
9600
    /**
9601
     * Fetches tuple statistics for all indexes on a table.
9602
     *
9603
     * @param $table The table to fetch index stats for
9604
     *
9605
     * @return A recordset
9606
     */
9607 View Code Duplication
    public function getStatsIndexTuples($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9608
    {
9609
        $c_schema = $this->_schema;
9610
        $this->clean($c_schema);
9611
        $this->clean($table);
9612
9613
        $sql = "SELECT * FROM pg_stat_all_indexes
9614
			WHERE schemaname='{$c_schema}' AND relname='{$table}' ORDER BY indexrelname";
9615
9616
        return $this->selectSet($sql);
9617
    }
9618
9619
    /**
9620
     * Fetches I/0 statistics for all indexes on a table.
9621
     *
9622
     * @param $table The table to fetch index stats for
9623
     *
9624
     * @return A recordset
9625
     */
9626 View Code Duplication
    public function getStatsIndexIO($table)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
9627
    {
9628
        $c_schema = $this->_schema;
9629
        $this->clean($c_schema);
9630
        $this->clean($table);
9631
9632
        $sql = "SELECT * FROM pg_statio_all_indexes
9633
			WHERE schemaname='{$c_schema}' AND relname='{$table}'
9634
			ORDER BY indexrelname";
9635
9636
        return $this->selectSet($sql);
9637
    }
9638
9639
    public function hasAggregateSortOp()
9640
    {
9641
        return true;
9642
    }
9643
9644
    public function hasAlterAggregate()
9645
    {
9646
        return true;
9647
    }
9648
9649
    public function hasAlterColumnType()
9650
    {
9651
        return true;
9652
    }
9653
9654
    public function hasAlterDatabaseOwner()
9655
    {
9656
        return true;
9657
    }
9658
9659
    public function hasAlterSchema()
9660
    {
9661
        return true;
9662
    }
9663
9664
    public function hasAlterSchemaOwner()
9665
    {
9666
        return true;
9667
    }
9668
9669
    public function hasAlterSequenceSchema()
9670
    {
9671
        return true;
9672
    }
9673
9674
    public function hasAlterSequenceStart()
9675
    {
9676
        return true;
9677
    }
9678
9679
    public function hasAlterTableSchema()
9680
    {
9681
        return true;
9682
    }
9683
9684
    public function hasAutovacuum()
9685
    {
9686
        return true;
9687
    }
9688
9689
    public function hasCreateTableLike()
9690
    {
9691
        return true;
9692
    }
9693
9694
    public function hasDisableTriggers()
9695
    {
9696
        return true;
9697
    }
9698
9699
    public function hasAlterDomains()
9700
    {
9701
        return true;
9702
    }
9703
9704
    public function hasEnumTypes()
9705
    {
9706
        return true;
9707
    }
9708
9709
    public function hasFTS()
9710
    {
9711
        return true;
9712
    }
9713
9714
    public function hasFunctionCosting()
9715
    {
9716
        return true;
9717
    }
9718
9719
    public function hasFunctionGUC()
9720
    {
9721
        return true;
9722
    }
9723
9724
    public function hasNamedParams()
9725
    {
9726
        return true;
9727
    }
9728
9729
    public function hasPrepare()
9730
    {
9731
        return true;
9732
    }
9733
9734
    public function hasPreparedXacts()
9735
    {
9736
        return true;
9737
    }
9738
9739
    public function hasRecluster()
9740
    {
9741
        return true;
9742
    }
9743
9744
    public function hasServerAdminFuncs()
9745
    {
9746
        return true;
9747
    }
9748
9749
    public function hasQueryCancel()
9750
    {
9751
        return true;
9752
    }
9753
9754
    public function hasUserRename()
9755
    {
9756
        return true;
9757
    }
9758
9759
    public function hasUserSignals()
9760
    {
9761
        return true;
9762
    }
9763
9764
    public function hasVirtualTransactionId()
9765
    {
9766
        return true;
9767
    }
9768
9769
    public function hasAlterDatabase()
9770
    {
9771
        return $this->hasAlterDatabaseRename();
9772
    }
9773
9774
    public function hasAlterDatabaseRename()
9775
    {
9776
        return true;
9777
    }
9778
9779
    public function hasDatabaseCollation()
9780
    {
9781
        return true;
9782
    }
9783
9784
    public function hasMagicTypes()
9785
    {
9786
        return true;
9787
    }
9788
9789
    public function hasQueryKill()
9790
    {
9791
        return true;
9792
    }
9793
9794
    public function hasConcurrentIndexBuild()
9795
    {
9796
        return true;
9797
    }
9798
9799
    public function hasForceReindex()
9800
    {
9801
        return false;
9802
    }
9803
9804
    public function hasByteaHexDefault()
9805
    {
9806
        return true;
9807
    }
9808
}
9809