Projekt

Obecné

Profil

Stáhnout (55.7 KB) Statistiky
| Větev: | Tag: | Revize:
1
<?php
2

    
3
/**
4
 * A class that implements the DB interface for Postgres
5
 * Note: This class uses ADODB and returns RecordSets.
6
 *
7
 * $Id: Postgres73.php,v 1.154 2005/10/17 08:28:23 jollytoad Exp $
8
 */
9

    
10
// @@@ THOUGHT: What about inherits? ie. use of ONLY???
11

    
12
include_once('./classes/database/Postgres72.php');
13

    
14
class Postgres73 extends Postgres72 {
15

    
16
	var $major_version = 7.3;
17

    
18
	// Store the current schema
19
	var $_schema;
20

    
21
	// Last oid assigned to a system object
22
	var $_lastSystemOID = 16974;
23

    
24
	// Max object name length
25
	var $_maxNameLen = 63;
26

    
27
	// List of all legal privileges that can be applied to different types
28
	// of objects.
29
	var $privlist = array(
30
		'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
31
		'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
32
		'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
33
		'database' => array('CREATE', 'TEMPORARY', 'ALL PRIVILEGES'),
34
		'function' => array('EXECUTE', 'ALL PRIVILEGES'),
35
		'language' => array('USAGE', 'ALL PRIVILEGES'),
36
		'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES')
37
	);
38
	// Function properties
39
	var $funcprops = array( array('', 'VOLATILE', 'IMMUTABLE', 'STABLE'), 
40
							array('', 'CALLED ON NULL INPUT', 'RETURNS NULL ON NULL INPUT'),
41
							array('', 'SECURITY INVOKER', 'SECURITY DEFINER'));
42
	var $defaultprops = array('', '', '');
43

    
44
	// Select operators
45
	var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', 
46
									'SIMILAR TO' => 'i', 'NOT SIMILAR TO' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i', 'IS NULL' => 'p', 'IS NOT NULL' => 'p',
47
									'IN' => 'x', 'NOT IN' => 'x');
48

    
49
	/**
50
	 * Constructor
51
	 * @param $conn The database connection
52
	 */
53
	function Postgres73($conn) {
54
		$this->Postgres72($conn);
55
	}
56

    
57
	// Help functions
58
	
59
	function getHelpPages() {
60
		include_once('./help/PostgresDoc73.php');
61
		return $this->help_page;
62
	}
63

    
64
	// Schema functions
65
	
66
	/**
67
	 * Sets the current working schema.  Will also set class variable.
68
	 * @param $schema The the name of the schema to work in
69
	 * @return 0 success
70
	 */
71
	function setSchema($schema) {
72
		// Get the current schema search path, including 'pg_catalog'.
73
		$search_path = $this->getSearchPath();
74
		// Prepend $schema to search path
75
		array_unshift($search_path, $schema);
76
		$status = $this->setSearchPath($search_path);
77
		if ($status == 0) {
78
			$this->clean($schema);
79
			$this->_schema = $schema;
80
			return 0;
81
		}
82
		else return $status;
83
	}
84
	
85
	/**
86
	 * Sets the current schema search path
87
	 * @param $paths An array of schemas in required search order
88
	 * @return 0 success
89
	 * @return -1 Array not passed
90
	 * @return -2 Array must contain at least one item
91
	 */
92
	function setSearchPath($paths) {
93
		if (!is_array($paths)) return -1;
94
		elseif (sizeof($paths) == 0) return -2;		
95
		elseif (sizeof($paths) == 1 && $paths[0] == '') {
96
			// Need to handle empty paths in some cases
97
			$paths[0] = 'pg_catalog';
98
		}
99
		
100
		// Loop over all the paths to check that none are empty
101
		$temp = array();
102
		foreach ($paths as $schema) {
103
			if ($schema != '') $temp[] = $schema;
104
		}
105
		$this->fieldArrayClean($temp);
106

    
107
		$sql = 'SET SEARCH_PATH TO "' . implode('","', $temp) . '"';
108
		
109
		return $this->execute($sql);
110
	}
111
	
112
	/**
113
	 * Return the current schema search path
114
	 * @return Array of schema names
115
	 */
116
	function getSearchPath() {
117
		$sql = 'SELECT current_schemas(false) AS search_path';
118
		
119
		return $this->phpArray($this->selectField($sql, 'search_path'));
120
	}
121

    
122
	/**
123
	 * Return all schemas in the current database
124
	 * @return All schemas, sorted alphabetically - but with PUBLIC first (if it exists)
125
	 */
126
	function getSchemas() {
127
		global $conf, $slony;
128

    
129
		if (!$conf['show_system']) {
130
			$where = "WHERE nspname NOT LIKE 'pg\\\\_%'";
131
			if (isset($slony) && $slony->isEnabled()) {
132
				$temp = $slony->slony_schema;
133
				$this->clean($temp);
134
				$where .= " AND nspname != '{$temp}'";
135
			}
136
		}
137
		else $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
138
		$sql = "SELECT pn.nspname, pu.usename AS nspowner, pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
139
                  FROM pg_catalog.pg_namespace pn LEFT JOIN pg_catalog.pg_user pu ON (pn.nspowner = pu.usesysid)
140
				  {$where} ORDER BY nspname";
141

    
142
		return $this->selectSet($sql);
143
	}
144

    
145
	/**
146
	 * Return all information relating to a schema
147
	 * @param $schema The name of the schema
148
	 * @return Schema information
149
	 */
150
	function getSchemaByName($schema) {
151
		$this->clean($schema);
152
		$sql = "SELECT nspname, nspowner, nspacl, pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
153
                        FROM pg_catalog.pg_namespace pn
154
                        WHERE nspname='{$schema}'";
155
		return $this->selectSet($sql);
156
	}
157

    
158
	/**
159
	 * Creates a new schema.
160
	 * @param $schemaname The name of the schema to create
161
	 * @param $authorization (optional) The username to create the schema for.
162
	 * @param $comment (optional) If omitted, defaults to nothing
163
	 * @return 0 success
164
	 */
165
	function createSchema($schemaname, $authorization = '', $comment = '') {
166
		$this->fieldClean($schemaname);
167
		$this->fieldClean($authorization);
168
		$this->clean($comment);
169

    
170
		$sql = "CREATE SCHEMA \"{$schemaname}\"";
171
		if ($authorization != '') $sql .= " AUTHORIZATION \"{$authorization}\"";
172
		
173
		if ($comment != '') {
174
			$status = $this->beginTransaction();
175
			if ($status != 0) return -1;
176
		}
177
		
178
		// Create the new schema
179
		$status =  $this->execute($sql);
180
		if ($status != 0) {
181
			$this->rollbackTransaction();
182
			return -1;
183
		}
184

    
185
		// Set the comment
186
		if ($comment != '') {
187
			$status = $this->setComment('SCHEMA', $schemaname, '', $comment);
188
			if ($status != 0) {
189
				$this->rollbackTransaction();
190
				return -1;
191
			}
192
			
193
			return $this->endTransaction();
194
		}
195
		
196
		return 0;
197
	}
198
	
199
	/**
200
	 * Drops a schema.
201
	 * @param $schemaname The name of the schema to drop
202
	 * @param $cascade True to cascade drop, false to restrict
203
	 * @return 0 success
204
	 */
205
	function dropSchema($schemaname, $cascade) {
206
		$this->fieldClean($schemaname);
207
		
208
		$sql = "DROP SCHEMA \"{$schemaname}\"";
209
		if ($cascade) $sql .= " CASCADE";
210
		
211
		return $this->execute($sql);
212
	}
213

    
214
	/**
215
	 * Updates a schema.
216
	 * @param $schemaname The name of the schema to drop
217
	 * @param $comment The new comment for this schema
218
	 * @return 0 success
219
	 */
220
	function updateSchema($schemaname, $comment) {
221
		$this->fieldClean($schemaname);
222
		$this->clean($comment);
223
		return $this->setComment('SCHEMA', $schemaname, '', $comment);
224
	}
225

    
226
	/**
227
	 * Returns all available variable information.
228
	 * @return A recordset
229
	 */
230
	function getVariables() {
231
		$sql = "SHOW ALL";
232
		
233
		return $this->selectSet($sql);
234
	}
235
	
236
	/**
237
	 * Returns all available process information.
238
	 * @param $database (optional) Find only connections to specified database
239
	 * @return A recordset
240
	 */
241
	function getProcesses($database = null) {
242
		if ($database === null)
243
			$sql = "SELECT * FROM pg_catalog.pg_stat_activity ORDER BY datname, usename, procpid";
244
		else {
245
			$this->clean($database);
246
			$sql = "SELECT * FROM pg_catalog.pg_stat_activity WHERE datname='{$database}' ORDER BY usename, procpid";
247
		}
248
		
249
		return $this->selectSet($sql);
250
	}
251
	
252
	// Table functions
253

    
254
	/**
255
	 * Checks to see whether or not a table has a unique id column
256
	 * @param $table The table name
257
	 * @return True if it has a unique id, false otherwise
258
	 * @return -99 error
259
	 */
260
	function hasObjectID($table) {
261
		$this->clean($table);
262

    
263
		$sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}' 
264
			AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
265

    
266
		$rs = $this->selectSet($sql);
267
		if ($rs->recordCount() != 1) return -99;
268
		else {
269
			$rs->f['relhasoids'] = $this->phpBool($rs->f['relhasoids']);
270
			return $rs->f['relhasoids'];
271
		}
272
	}
273

    
274
	/**
275
	 * Given an array of attnums and a relation, returns an array mapping
276
	 * attribute number to attribute name.
277
	 * @param $table The table to get attributes for
278
	 * @param $atts An array of attribute numbers
279
	 * @return An array mapping attnum to attname
280
	 * @return -1 $atts must be an array
281
	 * @return -2 wrong number of attributes found
282
	 */
283
	function getAttributeNames($table, $atts) {
284
		$this->clean($table);
285
		$this->arrayClean($atts);
286

    
287
		if (!is_array($atts)) return -1;
288

    
289
		if (sizeof($atts) == 0) return array();
290

    
291
		$sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE 
292
			attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
293
			relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')) 
294
			AND attnum IN ('" . join("','", $atts) . "')";
295

    
296
		$rs = $this->selectSet($sql);
297
		if ($rs->recordCount() != sizeof($atts)) {
298
			return -2;
299
		}
300
		else {
301
			$temp = array();
302
			while (!$rs->EOF) {
303
				$temp[$rs->f['attnum']] = $rs->f['attname'];
304
				$rs->moveNext();
305
			}
306
			return $temp;
307
		}
308
	}
309

    
310
	/**
311
	 * Get the fields for uniquely identifying a row in a table
312
	 * @param $table The table for which to retrieve the identifier
313
	 * @return An array mapping attribute number to attribute name, empty for no identifiers
314
	 * @return -1 error
315
	 */
316
	function getRowIdentifier($table) {
317
		$oldtable = $table;
318
		$this->clean($table);
319
		
320
		$status = $this->beginTransaction();
321
		if ($status != 0) return -1;
322

    
323
		// Get the first primary or unique index (sorting primary keys first) that
324
		// is NOT a partial index.
325
		$sql = "SELECT indrelid, indkey FROM pg_catalog.pg_index WHERE indisunique AND 
326
			indrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
327
			relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
328
			AND indpred='' AND indproc='-' ORDER BY indisprimary DESC LIMIT 1";
329
		$rs = $this->selectSet($sql);
330

    
331
		// If none, check for an OID column.  Even though OIDs can be duplicated, the edit and delete row
332
		// functions check that they're only modiying a single row.  Otherwise, return empty array.
333
		if ($rs->recordCount() == 0) {			
334
			// Check for OID column
335
			$temp = array();
336
			if ($this->hasObjectID($table)) {
337
				$temp = array('oid');
338
			}
339
			$this->endTransaction();
340
			return $temp;
341
		}
342
		// Otherwise find the names of the keys
343
		else {
344
			$attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->f['indkey']));
345
			if (!is_array($attnames)) {
346
				$this->rollbackTransaction();
347
				return -1;
348
			}
349
			else {
350
				$this->endTransaction();
351
				return $attnames;
352
			}
353
		}			
354
	}
355

    
356
	/**
357
	 * Returns table information
358
	 * @param $table The name of the table
359
	 * @return A recordset
360
	 */
361
	function getTable($table) {
362
		$this->clean($table);
363
		
364
		$sql = "
365
			SELECT
366
			  c.relname, u.usename AS relowner,
367
			  pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
368
			FROM pg_catalog.pg_class c
369
			     LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
370
			     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
371
			WHERE c.relkind = 'r'
372
			      AND n.nspname = '{$this->_schema}'
373
			      AND c.relname = '{$table}'";		
374
			
375
		return $this->selectSet($sql);
376
	}
377
	
378
	/**
379
	 * Return all tables in current database (and schema)
380
	 * @param $all True to fetch all tables, false for just in current schema
381
	 * @return All tables, sorted alphabetically 
382
	 */
383
	function getTables($all = false) {
384
		if ($all) {
385
			// Exclude pg_catalog and information_schema tables
386
			$sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
387
					FROM pg_catalog.pg_tables 
388
					WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
389
					ORDER BY schemaname, tablename";
390
		} else {
391
			$sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, 
392
						pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
393
						reltuples::integer
394
					FROM pg_catalog.pg_class c
395
					LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
396
					WHERE c.relkind = 'r'
397
					AND nspname='{$this->_schema}'
398
					ORDER BY c.relname";
399
		}
400

    
401
		return $this->selectSet($sql);
402
	}
403

    
404
	/**
405
	 * Retrieve the attribute definition of a table
406
	 * @param $table The name of the table
407
	 * @param $field (optional) The name of a field to return
408
	 * @return All attributes in order
409
	 */
410
	function getTableAttributes($table, $field = '') {
411
		$this->clean($table);
412
		$this->clean($field);
413

    
414
		if ($field == '') {
415
			// This query is made much more complex by the addition of the 'attisserial' field.
416
			// The subquery to get that field checks to see if there is an internally dependent 
417
			// sequence on the field.
418
			$sql = "
419
				SELECT
420
					a.attname,
421
					pg_catalog.format_type(a.atttypid, a.atttypmod) as type, 
422
					a.atttypmod,
423
					a.attnotnull, a.atthasdef, adef.adsrc,
424
					a.attstattarget, a.attstorage, t.typstorage,
425
					(
426
						SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
427
						WHERE pd.objid=pc.oid 
428
						AND pd.classid=pc.tableoid 
429
						AND pd.refclassid=pc.tableoid
430
						AND pd.refobjid=a.attrelid
431
						AND pd.refobjsubid=a.attnum
432
						AND pd.deptype='i'
433
						AND pc.relkind='S'
434
					) IS NOT NULL AS attisserial,
435
					pg_catalog.col_description(a.attrelid, a.attnum) AS comment 
436

    
437
				FROM
438
					pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
439
					ON a.attrelid=adef.adrelid
440
					AND a.attnum=adef.adnum
441
					LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
442
				WHERE 
443
					a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
444
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
445
						nspname = '{$this->_schema}'))
446
					AND a.attnum > 0 AND NOT a.attisdropped
447
				ORDER BY a.attnum";
448
		}
449
		else {
450
			$sql = "
451
				SELECT
452
					a.attname,
453
					pg_catalog.format_type(a.atttypid, a.atttypmod) as type, 
454
					pg_catalog.format_type(a.atttypid, NULL) as base_type,
455
					a.atttypmod,
456
					a.attnotnull, a.atthasdef, adef.adsrc,
457
					a.attstattarget, a.attstorage, t.typstorage,
458
					pg_catalog.col_description(a.attrelid, a.attnum) AS comment
459
				FROM
460
					pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
461
					ON a.attrelid=adef.adrelid
462
					AND a.attnum=adef.adnum
463
					LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
464
				WHERE
465
					a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
466
						AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
467
						nspname = '{$this->_schema}'))
468
					AND a.attname = '{$field}'";
469
		}
470

    
471
		return $this->selectSet($sql);
472
	}
473

    
474
	/**
475
	 * Drops a column from a table
476
	 * @param $table The table from which to drop a column
477
	 * @param $column The column to be dropped
478
	 * @param $cascade True to cascade drop, false to restrict
479
	 * @return 0 success
480
	 */
481
	function dropColumn($table, $column, $cascade) {
482
		$this->fieldClean($table);
483
		$this->fieldClean($column);
484

    
485
		$sql = "ALTER TABLE \"{$table}\" DROP COLUMN \"{$column}\"";
486
		if ($cascade) $sql .= " CASCADE";		
487

    
488
		return $this->execute($sql);
489
	}
490

    
491
	/**
492
	 * Sets whether or not a column can contain NULLs
493
	 * @param $table The table that contains the column
494
	 * @param $column The column to alter
495
	 * @param $state True to set null, false to set not null
496
	 * @return 0 success
497
	 */
498
	function setColumnNull($table, $column, $state) {
499
		$this->fieldClean($table);
500
		$this->fieldClean($column);
501

    
502
		$sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" " . (($state) ? 'DROP' : 'SET') . " NOT NULL";
503

    
504
		return $this->execute($sql);
505
	}
506

    
507
	// Inheritance functions
508
	
509
	/**
510
	 * Finds the names and schemas of parent tables (in order)
511
	 * @param $table The table to find the parents for
512
	 * @return A recordset
513
	 */
514
	function getTableParents($table) {
515
		$this->clean($table);
516
		
517
		$sql = "
518
			SELECT 
519
				pn.nspname, relname
520
			FROM
521
				pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
522
			WHERE
523
				pc.oid=pi.inhparent
524
				AND pc.relnamespace=pn.oid
525
				AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
526
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
527
			ORDER BY
528
				pi.inhseqno
529
		";
530
		
531
		return $this->selectSet($sql);					
532
	}	
533

    
534

    
535
	/**
536
	 * Finds the names and schemas of child tables
537
	 * @param $table The table to find the children for
538
	 * @return A recordset
539
	 */
540
	function getTableChildren($table) {
541
		$this->clean($table);
542
		
543
		$sql = "
544
			SELECT 
545
				pn.nspname, relname
546
			FROM
547
				pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
548
			WHERE
549
				pc.oid=pi.inhrelid
550
				AND pc.relnamespace=pn.oid
551
				AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
552
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
553
		";
554
		
555
		return $this->selectSet($sql);					
556
	}	
557

    
558
	// View functions
559
	
560
	/**
561
	 * Returns a list of all views in the database
562
	 * @return All views
563
	 */
564
	function getViews() {
565
		$sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, 
566
                          pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
567
                        FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
568
                        WHERE (n.nspname='{$this->_schema}') AND (c.relkind = 'v'::\"char\")  ORDER BY relname";
569

    
570
		return $this->selectSet($sql);
571
	}
572
	
573
	/**
574
	 * Returns all details for a particular view
575
	 * @param $view The name of the view to retrieve
576
	 * @return View info
577
	 */
578
	function getView($view) {
579
		$this->clean($view);
580

    
581
		$sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner, 
582
                          pg_catalog.pg_get_viewdef(c.oid) AS vwdefinition, pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
583
                        FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
584
                        WHERE (c.relname = '$view')
585
                        AND n.nspname='{$this->_schema}'";
586
 
587
		return $this->selectSet($sql);
588
	}
589
	
590
	/**
591
	 * Updates a view.
592
	 * @param $viewname The name fo the view to update
593
	 * @param $definition The new definition for the view
594
	 * @return 0 success
595
	 * @return -1 transaction error
596
	 * @return -2 drop view error
597
	 * @return -3 create view error
598
	 */
599
	function setView($viewname, $definition,$comment) {
600
                return $this->createView($viewname, $definition, true, $comment);
601
	}
602

    
603
	// Sequence functions
604

    
605
	/**
606
	 * Returns all sequences in the current database
607
	 * @return A recordset
608
	 */
609
	function getSequences($all = false) {
610
		if ($all) {
611
			// Exclude pg_catalog and information_schema tables
612
			$sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
613
				FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
614
				WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
615
				AND c.relkind = 'S' 
616
				AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast') 
617
				ORDER BY nspname, seqname";
618
		} else {
619
			$sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment
620
				FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
621
				WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
622
				AND c.relkind = 'S' AND n.nspname='{$this->_schema}' ORDER BY seqname";
623
		}
624
			
625
		return $this->selectSet( $sql );
626
	}
627

    
628
	/**
629
	 * Returns properties of a single sequence
630
	 * @param $sequence Sequence name
631
	 * @return A recordset
632
	 */
633
	function getSequence($sequence) {
634
		$this->fieldClean($sequence);
635
	
636
		$sql = "SELECT sequence_name AS seqname, *, pg_catalog.obj_description(s.tableoid, 'pg_class') AS seqcomment FROM \"{$sequence}\" AS s"; 
637
		
638
		return $this->selectSet( $sql );
639
	}
640

    
641
	/**
642
	 * Grabs a list of indexes for a table
643
	 * @param $table The name of a table whose indexes to retrieve
644
	 * @param $unique Only get unique/pk indexes
645
	 * @return A recordset
646
	 */
647
	function getIndexes($table = '', $unique = false) {
648
		$this->clean($table);
649
		
650
		$sql = "SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
651
			pg_catalog.pg_get_indexdef(i.indexrelid) AS inddef
652
			FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
653
			WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid) 
654
			AND c.oid = i.indrelid AND i.indexrelid = c2.oid
655
		";
656
		if ($unique) $sql .= " AND i.indisunique ";
657
		$sql .= " ORDER BY c2.relname";
658

    
659
		return $this->selectSet($sql);
660
	}
661

    
662
	/**
663
	 * Grabs a single trigger
664
	 * @param $table The name of a table whose triggers to retrieve
665
	 * @param $trigger The name of the trigger to retrieve
666
	 * @return A recordset
667
	 */
668
	function getTrigger($table, $trigger) {
669
		$this->clean($table);
670
		$this->clean($trigger);
671

    
672
		$sql = "SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c
673
					WHERE t.tgrelid=c.oid
674
					AND c.relname='{$table}'
675
					AND t.tgname='{$trigger}'
676
					AND c.relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
677

    
678
		return $this->selectSet($sql);
679
	}
680

    
681
	/**
682
	 * Grabs a list of triggers on a table
683
	 * @param $table The name of a table whose triggers to retrieve
684
	 * @return A recordset
685
	 */
686
	function getTriggers($table = '') {
687
		$this->clean($table);
688

    
689
		$sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype, 
690
			t.tgargs, t.tgnargs, t.tgconstrrelid, 
691
			(SELECT relname FROM pg_catalog.pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
692
			p.proname AS tgfname, c.relname, NULL AS tgdef
693
			FROM pg_catalog.pg_trigger t LEFT JOIN pg_catalog.pg_proc p
694
			ON t.tgfoid=p.oid, pg_catalog.pg_class c
695
			WHERE t.tgrelid=c.oid
696
			AND c.relname='{$table}'
697
			AND c.relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
698
			AND (NOT tgisconstraint OR NOT EXISTS
699
			(SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
700
			ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
701
			WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))";
702

    
703
		return $this->selectSet($sql);
704
	}
705
	
706
	/**
707
	 * Alters a trigger
708
	 * @param $table The name of the table containing the trigger
709
	 * @param $trigger The name of the trigger to alter
710
	 * @param $name The new name for the trigger
711
	 * @return 0 success
712
	 */
713
	function alterTrigger($table, $trigger, $name) {
714
		$this->fieldClean($table);
715
		$this->fieldClean($trigger);
716
		$this->fieldClean($name);
717
		
718
		$sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$table}\" RENAME TO \"{$name}\"";
719
		
720
		return $this->execute($sql);
721
	}	
722

    
723
	// Function functions
724

    
725
	/**
726
	 * Returns a list of all functions in the database
727
	 * @param $all If true, will find all available functions, if false just those in search path
728
	 * @param $type If not null, will find all functions with return value = type 
729
	 *
730
  	 * @return All functions
731
	 */
732
	function getFunctions($all = false, $type = null) {
733
		if ($all) {
734
			$where = 'pg_catalog.pg_function_is_visible(p.oid)';
735
			$distinct = 'DISTINCT ON (p.proname)';
736
			
737
			if ($type) {
738
				$where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
739
			}
740
		}
741
		else {
742
			$where = "n.nspname = '{$this->_schema}'";
743

    
744
			$distinct = '';
745
		}
746

    
747
		$sql = "SELECT
748
				{$distinct}
749
				p.oid AS prooid,
750
				p.proname,
751
				p.proretset,
752
				pg_catalog.format_type(p.prorettype, NULL) AS proresult,
753
				pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
754
				pl.lanname AS prolanguage,
755
				pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment,
756
				p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
757
				CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
758
				u.usename AS proowner
759
			FROM pg_catalog.pg_proc p
760
				INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
761
				INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
762
				LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
763
			WHERE NOT p.proisagg
764
				AND {$where}
765
			ORDER BY p.proname, proresult
766
			";
767

    
768
		return $this->selectSet($sql);
769
	}
770

    
771
	/**
772
	 * Returns all details for a particular function
773
	 * @param $func The name of the function to retrieve
774
	 * @return Function info
775
	 */
776
	function getFunction($function_oid) {
777
		$this->clean($function_oid);
778
		
779
		$sql = "SELECT 
780
					pc.oid AS prooid,
781
					proname,
782
					lanname as prolanguage,
783
					pg_catalog.format_type(prorettype, NULL) as proresult,
784
					prosrc,
785
					probin,
786
					proretset,
787
					proisstrict,
788
					provolatile,
789
					prosecdef,
790
					pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
791
					pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment
792
				FROM
793
					pg_catalog.pg_proc pc, pg_catalog.pg_language pl
794
				WHERE 
795
					pc.oid = '$function_oid'::oid
796
				AND pc.prolang = pl.oid
797
				";
798
	
799
		return $this->selectSet($sql);
800
	}
801
	
802
	/** 
803
	 * Returns an array containing a function's properties
804
	 * @param $f The array of data for the function
805
	 * @return An array containing the properties
806
	 */
807
	function getFunctionProperties($f) {
808
		$temp = array();
809
		
810
		// Volatility
811
		if ($f['provolatile'] == 'v')
812
			$temp[] = 'VOLATILE';
813
		elseif ($f['provolatile'] == 'i')
814
			$temp[] = 'IMMUTABLE';
815
		elseif ($f['provolatile'] == 's')
816
			$temp[] = 'STABLE';
817
		else
818
			return -1;
819
		
820
		// Null handling
821
		$f['proisstrict'] = $this->phpBool($f['proisstrict']);
822
		if ($f['proisstrict'])
823
			$temp[] = 'RETURNS NULL ON NULL INPUT';
824
		else
825
			$temp[] = 'CALLED ON NULL INPUT';
826
		
827
		// Security
828
		$f['prosecdef'] = $this->phpBool($f['prosecdef']);
829
		if ($f['prosecdef'])
830
			$temp[] = 'SECURITY DEFINER';
831
		else
832
			$temp[] = 'SECURITY INVOKER';
833
			
834
		return $temp;
835
	}	
836

    
837
	/**
838
	 * Returns a list of all functions that can be used in triggers
839
	 */
840
	function getTriggerFunctions() {
841
		return $this->getFunctions(true, 'trigger');
842
	}
843

    
844
	/**
845
	 * Creates a new function.
846
	 * @param $funcname The name of the function to create
847
	 * @param $args A comma separated string of types
848
	 * @param $returns The return type
849
	 * @param $definition The definition for the new function
850
	 * @param $language The language the function is written for
851
	 * @param $flags An array of optional flags
852
	 * @param $setof True if it returns a set, false otherwise
853
	 * @param $replace (optional) True if OR REPLACE, false for normal
854
	 * @return 0 success
855
	 */
856
	function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $replace = false) {
857
		$this->fieldClean($funcname);
858
		$this->clean($args);
859
		$this->clean($language);
860
		$this->arrayClean($flags);
861

    
862
		$sql = "CREATE";
863
		if ($replace) $sql .= " OR REPLACE";
864
		$sql .= " FUNCTION \"{$funcname}\" (";
865
		
866
		if ($args != '')
867
			$sql .= $args;
868

    
869
		// For some reason, the returns field cannot have quotes...
870
		$sql .= ") RETURNS ";
871
		if ($setof) $sql .= "SETOF ";
872
		$sql .= "{$returns} AS ";
873
		
874
		if (is_array($definition)) {
875
			$this->arrayClean($definition);
876
			$sql .= "'" . $definition[0] . "'";
877
			if ($definition[1]) {
878
				$sql .= ",'" . $definition[1] . "'";
879
			}
880
		} else {
881
			$this->clean($definition);
882
			$sql .= "'" . $definition . "'";
883
		}
884
		
885
		$sql .= " LANGUAGE \"{$language}\"";
886
		
887
		// Add flags
888
		foreach ($flags as  $v) {
889
			// Skip default flags
890
			if ($v == '') continue;
891
			else $sql .= "\n{$v}";
892
		}
893

    
894
		return $this->execute($sql);
895
	}
896
	
897
	// Type functions
898

    
899
	/**
900
	 * Returns a list of all types in the database
901
	 * @param $all If true, will find all available functions, if false just those in search path
902
	 * @param $tabletypes If true, will include table types
903
	 * @param $domains If true, will include domains
904
	 * @return A recordet
905
	 */
906
	function getTypes($all = false, $tabletypes = false, $domains = false) {
907
		if ($all)
908
			$where = 'pg_catalog.pg_type_is_visible(t.oid)';
909
		else
910
			$where = "n.nspname = '{$this->_schema}'";
911
		// Never show system table types
912
		$where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg\\\\_%')";
913

    
914
		// Create type filter
915
		$tqry = "'c'";
916
		if ($tabletypes)
917
			$tqry .= ", 'r', 'v'";
918

    
919
		// Create domain filter
920
		if (!$domains)
921
			$where .= " AND t.typtype != 'd'";
922
			
923
		$sql = "SELECT
924
				t.typname AS basename,
925
				pg_catalog.format_type(t.oid, NULL) AS typname,
926
				pu.usename AS typowner,
927
				t.typtype,
928
				pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment
929
			FROM (pg_catalog.pg_type t
930
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace)
931
				LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid
932
			WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2}))	 
933
			AND t.typname !~ '^_'
934
			AND {$where}			
935
			ORDER BY typname
936
		";
937

    
938
		return $this->selectSet($sql);
939
	}
940

    
941
	/**
942
	 * Creates a new composite type in the database
943
	 * @param $name The name of the type
944
	 * @param $fields The number of fields
945
	 * @param $field An array of field names
946
	 * @param $type An array of field types
947
	 * @param $array An array of '' or '[]' for each type if it's an array or not
948
	 * @param $length An array of field lengths
949
	 * @param $colcomment An array of comments
950
	 * @param $typcomment Type comment
951
	 * @return 0 success
952
	 * @return -1 no fields supplied
953
	 */
954
	function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment) {
955
		$this->fieldClean($name);
956
		$this->clean($typcomment);
957

    
958
		$status = $this->beginTransaction();
959
		if ($status != 0) return -1;
960

    
961
		$found = false;
962
		$first = true;
963
		$comment_sql = ''; // Accumulate comments for the columns
964
		$sql = "CREATE TYPE \"{$name}\" AS (";
965
		for ($i = 0; $i < $fields; $i++) {
966
			$this->fieldClean($field[$i]);
967
			$this->clean($type[$i]);
968
			$this->clean($length[$i]);
969
			$this->clean($colcomment[$i]);
970

    
971
			// Skip blank columns - for user convenience
972
			if ($field[$i] == '' || $type[$i] == '') continue;
973
			// If not the first column, add a comma
974
			if (!$first) $sql .= ", ";
975
			else $first = false;
976
			
977
			switch ($type[$i]) {
978
				// Have to account for weird placing of length for with/without
979
				// time zone types
980
				case 'timestamp with time zone':
981
				case 'timestamp without time zone':
982
					$qual = substr($type[$i], 9);
983
					$sql .= "\"{$field[$i]}\" timestamp";
984
					if ($length[$i] != '') $sql .= "({$length[$i]})";
985
					$sql .= $qual;
986
					break;
987
				case 'time with time zone':
988
				case 'time without time zone':
989
					$qual = substr($type[$i], 4);
990
					$sql .= "\"{$field[$i]}\" time";
991
					if ($length[$i] != '') $sql .= "({$length[$i]})";
992
					$sql .= $qual;
993
					break;
994
				default:
995
					$sql .= "\"{$field[$i]}\" {$type[$i]}";
996
					if ($length[$i] != '') $sql .= "({$length[$i]})";
997
			}
998
			// Add array qualifier if necessary
999
			if ($array[$i] == '[]') $sql .= '[]';
1000

    
1001
			if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
1002

    
1003
			$found = true;
1004
		}
1005
		
1006
		if (!$found) return -1;
1007
		
1008
		$sql .= ")";
1009
				
1010
		$status = $this->execute($sql);
1011
		if ($status) {
1012
			$this->rollbackTransaction();
1013
			return -1;
1014
		}
1015

    
1016
		if ($typcomment != '') {
1017
			$status = $this->setComment('TYPE', $name, '', $typcomment, true);
1018
			if ($status) {
1019
				$this->rollbackTransaction();
1020
				return -1;
1021
			}
1022
		}
1023

    
1024
		if ($comment_sql != '') {
1025
			$status = $this->execute($comment_sql);
1026
			if ($status) {
1027
				$this->rollbackTransaction();
1028
				return -1;
1029
			}
1030
		}
1031
		return $this->endTransaction();
1032
		
1033
	}
1034
		
1035
	// Rule functions
1036
	
1037
	/**
1038
	 * Removes a rule from a table OR view
1039
	 * @param $rule The rule to drop
1040
	 * @param $relation The relation from which to drop
1041
	 * @param $cascade True to cascade drop, false to restrict
1042
	 * @return 0 success
1043
	 */
1044
	function dropRule($rule, $relation, $cascade) {
1045
		$this->fieldClean($rule);
1046
		$this->fieldClean($relation);
1047

    
1048
		$sql = "DROP RULE \"{$rule}\" ON \"{$relation}\"";
1049
		if ($cascade) $sql .= " CASCADE";
1050

    
1051
		return $this->execute($sql);
1052
	}
1053

    
1054
	/**
1055
	 * Returns a list of all rules on a table OR view
1056
	 * @param $table The table to find rules for
1057
	 * @return A recordset
1058
	 */
1059
	function getRules($table) {
1060
		$this->clean($table);
1061

    
1062
		$sql = "SELECT 
1063
				*
1064
			FROM 
1065
				pg_catalog.pg_rules
1066
			WHERE
1067
				schemaname='{$this->_schema}'
1068
				AND tablename='{$table}'
1069
			ORDER BY
1070
				rulename
1071
		";
1072

    
1073
		return $this->selectSet($sql);
1074
	}
1075
	
1076
	/**
1077
	 * Edits a rule on a table OR view
1078
	 * @param $name The name of the new rule
1079
	 * @param $event SELECT, INSERT, UPDATE or DELETE
1080
	 * @param $table Table on which to create the rule
1081
	 * @param $where When to execute the rule, '' indicates always
1082
	 * @param $instead True if an INSTEAD rule, false otherwise
1083
	 * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
1084
	 * @param $action The action to take
1085
	 * @return 0 success
1086
	 * @return -1 invalid event
1087
	 */
1088
	function setRule($name, $event, $table, $where, $instead, $type, $action) {
1089
		return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true);
1090
	}
1091

    
1092
	// Constraint functions
1093

    
1094
	/**
1095
	 * A function for getting all columns linked by foreign keys given a group of tables
1096
	 * @param $tables multi dimensional assoc array that holds schema and table name
1097
	 * @return An array of linked tables and columns
1098
	 * @return -1 $tables isn't an array
1099
	 */
1100
	 function getLinkingKeys($tables) {
1101
		if (!is_array($tables)) return -1;
1102

    
1103
		
1104
		$tables_list = "'{$tables[0]['schemaname']}'";
1105
		$schema_list = "'{$tables[0]['tablename']}'";
1106
		$schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'";
1107
		for ($i = 1; $i < sizeof($tables); $i++) {
1108
			$tables_list .= ", '{$tables[$i]['tablename']}'";
1109
			$schema_list .= ", '{$tables[$i]['schemaname']}'";
1110
			$schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'";
1111
		}
1112
		$maxDimension = 1;
1113

    
1114
		$sql = "
1115
			SELECT DISTINCT
1116
				array_dims(pc.conkey) AS arr_dim,
1117
				pgc1.relname AS p_table
1118
			FROM
1119
				pg_catalog.pg_constraint AS pc,
1120
				pg_catalog.pg_class AS pgc1
1121
			WHERE
1122
				pc.contype = 'f'
1123
				AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
1124
				AND pgc1.relname IN ($tables_list)
1125
			";
1126
		
1127
		//parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
1128
		$rs = $this->selectSet($sql);
1129
		while (!$rs->EOF) {
1130
			$arrData = explode(':', $rs->fields['arr_dim']);
1131
			$tmpDimension = intval(substr($arrData[1], 0, strlen($arrData[1] - 1)));
1132
			$maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
1133
			$rs->MoveNext();
1134
		}
1135
		
1136
		//we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
1137
		$cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
1138
		for ($i = 2; $i <= $maxDimension; $i++) {
1139
			$cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) ";
1140
		}
1141
		$cons_str .= ') ';
1142
		
1143
		$sql = "
1144
			SELECT
1145
				pgc1.relname AS p_table,
1146
				pgc2.relname AS f_table,
1147
				pfield.attname AS p_field,
1148
				cfield.attname AS f_field,
1149
				pgns1.nspname AS p_schema,
1150
				pgns2.nspname AS f_schema
1151
			FROM
1152
				pg_catalog.pg_constraint AS pc,
1153
				pg_catalog.pg_class AS pgc1,
1154
				pg_catalog.pg_class AS pgc2,
1155
				pg_catalog.pg_attribute AS pfield,
1156
				pg_catalog.pg_attribute AS cfield,
1157
				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns1,
1158
 				(SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns2
1159
			WHERE
1160
				pc.contype = 'f'
1161
				AND pgc1.relnamespace = pgns1.ns_id
1162
 				AND pgc2.relnamespace = pgns2.ns_id
1163
				AND pc.conrelid = pgc1.relfilenode
1164
				AND pc.confrelid = pgc2.relfilenode
1165
				AND pfield.attrelid = pc.conrelid
1166
				AND cfield.attrelid = pc.confrelid
1167
				AND $cons_str
1168
				AND pgns1.nspname || '.' || pgc1.relname IN ($schema_tables_list)
1169
				AND pgns2.nspname || '.' || pgc2.relname IN ($schema_tables_list)				
1170
		";		
1171
		return $this->selectSet($sql);
1172
	 }
1173

    
1174
	/**
1175
	 * Returns a list of all constraints on a table
1176
	 * @param $table The table to find rules for
1177
	 * @return A recordset
1178
	 */
1179
	function getConstraints($table) {
1180
		$this->clean($table);
1181

    
1182
		/* This query finds all foreign key and check constraints in the pg_constraint
1183
		 * table, and unions that with all indexes that are the basis for unique or
1184
		 * primary key constraints. */
1185
		$sql = "
1186
			SELECT conname, consrc, contype, indkey, indisclustered FROM (
1187
				SELECT
1188
					conname,
1189
					CASE WHEN contype='f' THEN
1190
						pg_catalog.pg_get_constraintdef(oid)
1191
					ELSE
1192
						'CHECK (' || consrc || ')'
1193
					END AS consrc,
1194
					contype,
1195
					conrelid AS relid,
1196
					NULL AS indkey,
1197
					FALSE AS indisclustered
1198
				FROM
1199
					pg_catalog.pg_constraint
1200
				WHERE
1201
					contype IN ('f', 'c')
1202
				UNION ALL
1203
				SELECT
1204
					pc.relname,
1205
					NULL,
1206
					CASE WHEN indisprimary THEN
1207
						'p'
1208
					ELSE
1209
						'u'
1210
					END,
1211
					pi.indrelid,
1212
					indkey,
1213
					pi.indisclustered
1214
				FROM
1215
					pg_catalog.pg_class pc,
1216
					pg_catalog.pg_index pi
1217
				WHERE
1218
					pc.oid=pi.indexrelid
1219
					AND EXISTS (
1220
						SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
1221
						ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
1222
						WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
1223
				)
1224
			) AS sub
1225
			WHERE relid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
1226
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
1227
					WHERE nspname='{$this->_schema}'))
1228
			ORDER BY
1229
				1
1230
		";
1231

    
1232
		return $this->selectSet($sql);
1233
	}
1234

    
1235
	/**
1236
	 * Removes a constraint from a relation
1237
	 * @param $constraint The constraint to drop
1238
	 * @param $relation The relation from which to drop
1239
	 * @param $type The type of constraint (c, f, u or p)
1240
	 * @param $cascade True to cascade drop, false to restrict
1241
	 * @return 0 success
1242
	 */
1243
	function dropConstraint($constraint, $relation, $type, $cascade) {
1244
		$this->fieldClean($constraint);
1245
		$this->fieldClean($relation);
1246

    
1247
		$sql = "ALTER TABLE \"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
1248
		if ($cascade) $sql .= " CASCADE";
1249

    
1250
		return $this->execute($sql);
1251
	}
1252

    
1253
	/**
1254
	 * Finds the foreign keys that refer to the specified table
1255
	 * @param $table The table to find referrers for
1256
	 * @return A recordset
1257
	 */
1258
	function getReferrers($table) {
1259
		$this->clean($table);
1260

    
1261
		$status = $this->beginTransaction();
1262
		if ($status != 0) return -1;
1263

    
1264
		$sql = "
1265
			SELECT
1266
				pn.nspname,
1267
				pl.relname,
1268
				pc.conname,
1269
				pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
1270
			FROM
1271
				pg_catalog.pg_constraint pc,
1272
				pg_catalog.pg_namespace pn,
1273
				pg_catalog.pg_class pl
1274
			WHERE
1275
				pc.connamespace = pn.oid
1276
				AND pc.conrelid = pl.oid
1277
				AND pc.contype = 'f'
1278
				AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
1279
					AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
1280
					WHERE nspname='{$this->_schema}'))
1281
			ORDER BY 1,2,3
1282
		";
1283

    
1284
		return $this->selectSet($sql);
1285
	}
1286

    
1287
	// Privilege functions
1288

    
1289
	/**
1290
	 * Grabs an array of users and their privileges for an object,
1291
	 * given its type.
1292
	 * @param $object The name of the object whose privileges are to be retrieved
1293
	 * @param $type The type of the object (eg. database, schema, relation, function or language)
1294
	 * @return Privileges array
1295
	 * @return -1 invalid type
1296
	 * @return -2 object not found
1297
	 * @return -3 unknown privilege type
1298
	 */
1299
	function getPrivileges($object, $type) {
1300
		$this->clean($object);
1301

    
1302
		switch ($type) {
1303
			case 'table':
1304
			case 'view':
1305
			case 'sequence':
1306
				$sql = "SELECT relacl AS acl FROM pg_catalog.pg_class WHERE relname='{$object}'
1307
						AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
1308
				break;
1309
			case 'database':
1310
				$sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
1311
				break;
1312
			case 'function':
1313
				// Since we fetch functions by oid, they are already constrained to
1314
				// the current schema.
1315
				$sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
1316
				break;
1317
			case 'language':
1318
				$sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
1319
				break;
1320
			case 'schema':
1321
				$sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
1322
				break;
1323
			case 'tablespace':
1324
				$sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
1325
				break;
1326
			default:
1327
				return -1;
1328
		}
1329

    
1330
		// Fetch the ACL for object
1331
		$acl = $this->selectField($sql, 'acl');
1332
		if ($acl == -1) return -2;
1333
		elseif ($acl == '' || $acl == null) return array();
1334
		else return $this->_parseACL($acl);
1335
	}
1336
	
1337
	// Domain functions
1338
	
1339
	/**
1340
	 * Gets all information for a single domain
1341
	 * @param $domain The name of the domain to fetch
1342
	 * @return A recordset
1343
	 */
1344
	function getDomain($domain) {
1345
		$this->clean($domain);
1346
		
1347
		$sql = "
1348
			SELECT
1349
				t.typname AS domname, 
1350
				pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
1351
				t.typnotnull AS domnotnull,
1352
				t.typdefault AS domdef,
1353
				pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
1354
				pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
1355
			FROM 
1356
				pg_catalog.pg_type t
1357
			WHERE 
1358
				t.typtype = 'd'
1359
				AND t.typname = '{$domain}'
1360
				AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
1361
					WHERE nspname = '{$this->_schema}')";
1362

    
1363
		return $this->selectSet($sql);		
1364
	}
1365
	
1366
	/**
1367
	 * Return all domains in current schema.  Excludes domain constraints.
1368
	 * @return All tables, sorted alphabetically 
1369
	 */
1370
	function getDomains() {
1371
		$sql = "		
1372
			SELECT
1373
				t.typname AS domname, 
1374
				pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
1375
				t.typnotnull AS domnotnull,
1376
				t.typdefault AS domdef,
1377
				pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
1378
				pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
1379
			FROM 
1380
				pg_catalog.pg_type t
1381
			WHERE 
1382
				t.typtype = 'd'
1383
				AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
1384
					WHERE nspname='{$this->_schema}')
1385
			ORDER BY t.typname";
1386

    
1387
		return $this->selectSet($sql);
1388
	}
1389

    
1390
	/**
1391
	 * Creates a domain
1392
	 * @param $domain The name of the domain to create
1393
	 * @param $type The base type for the domain
1394
	 * @param $length Optional type length
1395
	 * @param $array True for array type, false otherwise
1396
	 * @param $notnull True for NOT NULL, false otherwise
1397
	 * @param $default Default value for domain	
1398
	 * @param $check A CHECK constraint if there is one
1399
	 * @return 0 success
1400
	 */
1401
	function createDomain($domain, $type, $length, $array, $notnull, $default, $check) {
1402
		$this->fieldClean($domain);
1403
		
1404
		$sql = "CREATE DOMAIN \"{$domain}\" AS ";
1405

    
1406
		if ($length == '')
1407
			$sql .= $type;
1408
		else {
1409
			switch ($type) {
1410
				// Have to account for weird placing of length for with/without
1411
				// time zone types
1412
				case 'timestamp with time zone':
1413
				case 'timestamp without time zone':
1414
					$qual = substr($type, 9);
1415
					$sql .= "timestamp({$length}){$qual}";
1416
					break;
1417
				case 'time with time zone':
1418
				case 'time without time zone':
1419
					$qual = substr($type, 4);
1420
					$sql .= "time({$length}){$qual}";
1421
					break;
1422
				default:
1423
					$sql .= "{$type}({$length})";
1424
			}
1425
		}
1426
		
1427
		// Add array qualifier, if requested
1428
		if ($array) $sql .= '[]';
1429
		
1430
		if ($notnull) $sql .= ' NOT NULL';
1431
		if ($default != '') $sql .= " DEFAULT {$default}";
1432
		if ($this->hasDomainConstraints() && $check != '') $sql .= " CHECK ({$check})";
1433

    
1434
		return $this->execute($sql);
1435
	}
1436
	
1437
	/**
1438
	 * Drops a domain.
1439
	 * @param $domain The name of the domain to drop
1440
	 * @param $cascade True to cascade drop, false to restrict
1441
	 * @return 0 success
1442
	 */
1443
	function dropDomain($domain, $cascade) {
1444
		$this->fieldClean($domain);
1445

    
1446
		$sql = "DROP DOMAIN \"{$domain}\"";
1447
		if ($cascade) $sql .= " CASCADE";
1448

    
1449
		return $this->execute($sql);
1450
	}	
1451
	
1452
	// Find object functions
1453
	
1454
	/**
1455
	 * Searches all system catalogs to find objects that match a certain name.
1456
	 * @param $term The search term
1457
	 * @param $filter The object type to restrict to ('' means no restriction)
1458
	 * @return A recordset
1459
	 */
1460
	function findObject($term, $filter) {
1461
		global $conf;
1462

    
1463
		// Escape search term for ILIKE match
1464
		$term = str_replace('_', '\\_', $term);
1465
		$term = str_replace('%', '\\%', $term);
1466
		$this->clean($term);
1467
		$this->clean($filter);
1468

    
1469
		// Exclude system relations if necessary
1470
		if (!$conf['show_system']) {
1471
			// XXX: The mention of information_schema here is in the wrong place, but
1472
			// it's the quickest fix to exclude the info schema from 7.4
1473
			$where = " AND pn.nspname NOT LIKE 'pg\\\\_%' AND pn.nspname != 'information_schema'";
1474
			$lan_where = "AND pl.lanispl";
1475
		}
1476
		else {
1477
			$where = '';
1478
			$lan_where = '';
1479
		}
1480
		
1481
		// Apply outer filter
1482
		$sql = '';
1483
		if ($filter != '') {
1484
			$sql = "SELECT * FROM (";
1485
		}
1486
		
1487
		$sql .= "
1488
			SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name 
1489
				FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE '%{$term}%' {$where}
1490
			UNION ALL
1491
			SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
1492
				pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn 
1493
				WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE '%{$term}%' {$where}
1494
			UNION ALL
1495
			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,
1496
				pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid 
1497
				AND pa.attname ILIKE '%{$term}%' AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
1498
			UNION ALL
1499
			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 
1500
				WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE '%{$term}%' {$where}
1501
			UNION ALL
1502
			SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
1503
				pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid 
1504
				AND pi.indexrelid=pc2.oid
1505
				AND NOT EXISTS (
1506
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
1507
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
1508
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
1509
				)
1510
				AND pc2.relname ILIKE '%{$term}%' {$where}
1511
			UNION ALL
1512
			SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
1513
				pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
1514
				AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
1515
					SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
1516
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
1517
					WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
1518
				) END
1519
				AND pc2.conname ILIKE '%{$term}%' {$where}
1520
			UNION ALL
1521
			SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
1522
				pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
1523
				AND pc.conname ILIKE '%{$term}%' {$where}
1524
			UNION ALL
1525
			SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
1526
				pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
1527
					AND (NOT pt.tgisconstraint OR NOT EXISTS
1528
					(SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
1529
					ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
1530
					WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
1531
				AND pt.tgname ILIKE '%{$term}%' {$where}
1532
			UNION ALL
1533
			SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
1534
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
1535
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
1536
				WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
1537
			UNION ALL
1538
			SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
1539
				JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
1540
				LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
1541
				WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
1542
		";
1543

    
1544
		// Add advanced objects if show_advanced is set
1545
		if ($conf['show_advanced']) {
1546
			$sql .= "
1547
				UNION ALL
1548
				SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL, 
1549
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn 
1550
					WHERE pt.typnamespace=pn.oid AND typname ILIKE '%{$term}%'
1551
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
1552
					{$where}
1553
			 	UNION ALL
1554
				SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn 
1555
					WHERE po.oprnamespace=pn.oid AND oprname ILIKE '%{$term}%' {$where}
1556
				UNION ALL
1557
				SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
1558
					pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE '%{$term}%' {$where}
1559
				UNION ALL
1560
				SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
1561
					WHERE lanname ILIKE '%{$term}%' {$lan_where}
1562
				UNION ALL
1563
				SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
1564
					LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
1565
					WHERE p.proisagg AND p.proname ILIKE '%{$term}%' {$where}
1566
				UNION ALL
1567
				SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
1568
					pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
1569
					AND po.opcname ILIKE '%{$term}%' {$where}
1570
			";
1571
		}
1572
		// Otherwise just add domains
1573
		else {
1574
			$sql .= "
1575
				UNION ALL
1576
				SELECT 'DOMAIN', pt.oid, pn.nspname, NULL, 
1577
					pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn 
1578
					WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE '%{$term}%'
1579
					AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
1580
					{$where}
1581
			";
1582
		}
1583

    
1584
		if ($filter != '') {
1585
			// We use like to make RULE, CONSTRAINT and COLUMN searches work
1586
			$sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
1587
		}
1588

    
1589
		$sql .= "ORDER BY type, schemaname, relname, name";
1590

    
1591
		return $this->selectSet($sql);
1592
	}	
1593

    
1594
	// Operator functions
1595
	
1596
	/**
1597
	 * Returns a list of all operators in the database
1598
	 * @return All operators
1599
	 */	 
1600
	function getOperators() {
1601
		// We stick with the subselects here, as you cannot ORDER BY a regtype
1602
		$sql = "
1603
			SELECT
1604
            po.oid,
1605
				po.oprname,
1606
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
1607
				(SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
1608
				po.oprresult::pg_catalog.regtype AS resultname,
1609
		        pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment
1610
			FROM
1611
				pg_catalog.pg_operator po
1612
			WHERE
1613
				po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
1614
			ORDER BY
1615
				po.oprname, oprleftname, oprrightname
1616
		";
1617

    
1618
		return $this->selectSet($sql);
1619
	}	
1620

    
1621
	/**
1622
	 * Returns all details for a particular operator
1623
	 * @param $operator_oid The oid of the operator
1624
	 * @return Function info
1625
	 */
1626
	function getOperator($operator_oid) {
1627
		$this->clean($operator_oid);
1628

    
1629
		$sql = "
1630
			SELECT
1631
            po.oid,
1632
				po.oprname,
1633
				oprleft::pg_catalog.regtype AS oprleftname,
1634
				oprright::pg_catalog.regtype AS oprrightname,
1635
				oprresult::pg_catalog.regtype AS resultname,
1636
				po.oprcanhash,
1637
				oprcom::pg_catalog.regoperator AS oprcom,
1638
				oprnegate::pg_catalog.regoperator AS oprnegate,
1639
				oprlsortop::pg_catalog.regoperator AS oprlsortop,
1640
				oprrsortop::pg_catalog.regoperator AS oprrsortop,
1641
				oprltcmpop::pg_catalog.regoperator AS oprltcmpop,
1642
				oprgtcmpop::pg_catalog.regoperator AS oprgtcmpop,
1643
				po.oprcode::pg_catalog.regproc AS oprcode,
1644
				po.oprrest::pg_catalog.regproc AS oprrest,
1645
				po.oprjoin::pg_catalog.regproc AS oprjoin
1646
			FROM
1647
				pg_catalog.pg_operator po
1648
			WHERE
1649
				po.oid='{$operator_oid}'
1650
		";
1651
	
1652
		return $this->selectSet($sql);
1653
	}
1654

    
1655
	// Cast functions
1656
	
1657
	/**
1658
	 * Returns a list of all casts in the database
1659
	 * @return All casts
1660
	 */	 
1661
	function getCasts() {
1662
		global $conf;
1663
				
1664
		if ($conf['show_system'])
1665
			$where = '';
1666
		else
1667
			$where = "
1668
				AND n1.nspname NOT LIKE 'pg\\\\_%'
1669
				AND n2.nspname NOT LIKE 'pg\\\\_%'
1670
				AND n3.nspname NOT LIKE 'pg\\\\_%'
1671
			";
1672

    
1673
		$sql = "
1674
			SELECT
1675
				c.castsource::pg_catalog.regtype AS castsource,
1676
				c.casttarget::pg_catalog.regtype AS casttarget,
1677
				CASE WHEN c.castfunc=0 THEN NULL
1678
				ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc,
1679
				c.castcontext
1680
			FROM
1681
				(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),
1682
				pg_catalog.pg_type t1,
1683
				pg_catalog.pg_type t2,
1684
				pg_catalog.pg_namespace n1,
1685
				pg_catalog.pg_namespace n2				
1686
			WHERE
1687
				c.castsource=t1.oid
1688
				AND c.casttarget=t2.oid
1689
				AND t1.typnamespace=n1.oid
1690
				AND t2.typnamespace=n2.oid
1691
				{$where}
1692
			ORDER BY 1, 2
1693
		";
1694

    
1695
		return $this->selectSet($sql);
1696
	}	
1697

    
1698
	// Conversion functions
1699
	
1700
	/**
1701
	 * Returns a list of all conversions in the database
1702
	 * @return All conversions
1703
	 */	 
1704
	function getConversions() {
1705
		$sql = "
1706
			SELECT
1707
			       c.conname,
1708
			       pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding,
1709
			       pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding,
1710
			       c.condefault,
1711
			       pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment
1712
			FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n
1713
			WHERE n.oid = c.connamespace
1714
			      AND n.nspname='{$this->_schema}'
1715
			ORDER BY 1;
1716
		";
1717

    
1718
		return $this->selectSet($sql);
1719
	}
1720
	
1721
	// Language functions
1722
	
1723
	/**
1724
	 * Gets all languages
1725
	 * @param $all True to get all languages, regardless of show_system
1726
	 * @return A recordset
1727
	 */
1728
	function getLanguages($all = false) {
1729
		global $conf;
1730
		
1731
		if ($conf['show_system'] || $all)
1732
			$where = '';
1733
		else
1734
			$where = 'WHERE lanispl';
1735

    
1736
		$sql = "
1737
			SELECT
1738
				lanname,
1739
				lanpltrusted,
1740
				lanplcallfoid::pg_catalog.regproc AS lanplcallf
1741
			FROM
1742
				pg_catalog.pg_language
1743
			{$where}
1744
			ORDER BY
1745
				lanname
1746
		";
1747
		
1748
		return $this->selectSet($sql);
1749
	}
1750

    
1751
	// Aggregate functions
1752
	
1753
	/**
1754
	 * Gets all aggregates
1755
	 * @return A recordset
1756
	 */
1757
	function getAggregates() {
1758
		$sql = "
1759
			SELECT
1760
				p.proname,
1761
				CASE p.proargtypes[0]
1762
					WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype
1763
					THEN NULL
1764
					ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
1765
				END AS proargtypes,
1766
				pg_catalog.obj_description(p.oid, 'pg_proc') AS aggcomment
1767
			FROM pg_catalog.pg_proc p
1768
				LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
1769
			WHERE
1770
				p.proisagg
1771
				AND n.nspname='{$this->_schema}'
1772
			ORDER BY 1, 2
1773
		";
1774

    
1775
		return $this->selectSet($sql);
1776
	}
1777
	
1778
	// Operator Class functions
1779
	
1780
	/**
1781
	 * Gets all opclasses
1782
	 * @return A recordset
1783
	 */
1784
	function getOpClasses() {
1785
		$sql = "
1786
			SELECT
1787
				pa.amname,
1788
				po.opcname,
1789
				po.opcintype::pg_catalog.regtype AS opcintype,
1790
				po.opcdefault,
1791
				pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
1792
			FROM
1793
				pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
1794
			WHERE
1795
				po.opcamid=pa.oid
1796
				AND po.opcnamespace=pn.oid
1797
				AND pn.nspname='{$this->_schema}'
1798
			ORDER BY 1,2
1799
		";
1800

    
1801
		return $this->selectSet($sql);
1802
	}
1803

    
1804
	// Query functions
1805

    
1806
	/**
1807
	 * Returns explained version of a query
1808
	 * @param $query The query for which to get data
1809
	 * @param $analyze True to analyze as well
1810
	 */
1811
	function getExplainSQL($query, $analyze) {
1812
		$temp = "EXPLAIN ";
1813
		if ($analyze) $temp .= "ANALYZE ";
1814
		$temp .= $query;
1815
		return $temp;
1816
	}
1817
		
1818
	// Capabilities
1819
	function hasSchemas() { return true; }
1820
	function hasConversions() { return true; }
1821
	function hasIsClustered() { return true; }
1822
	function hasDropBehavior() { return true; }
1823
	function hasDropColumn() { return true; }
1824
	function hasDomains() { return true; }
1825
	function hasAlterTrigger() { return true; }
1826
	function hasCasts() { return true; }
1827
	function hasPrepare() { return true; }
1828
	function hasUserSessionDefaults() { return true; }
1829
	function hasVariables() { return true; }
1830
	function hasFullExplain() { return true; }
1831
	function hasForeignKeysInfo() { return true; }
1832
	function hasViewColumnRename() { return true; }
1833
	function hasUserAndDbVariables() { return true; }
1834
	function hasCompositeTypes() { return true; }	
1835
	function hasFuncPrivs() { return true; }
1836

    
1837
}
1838

    
1839
?>
(6-6/10)