summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume (ioguix) de Rorthais2008-10-28 15:10:45 +0000
committerGuillaume (ioguix) de Rorthais2008-10-28 15:10:45 +0000
commit914109fb251921a14e450b0cbd273bf92d918f1d (patch)
tree197dea6268190c83104c7f4984e3c6c2c692a554
parentfd73c5f02cd6a6980feab6ea04f46ec63fca5f6a (diff)
Reverse database classes Inheritance
- reverse database classes inheritance - remove pg 70, 71, 72 support - refactor alterTable, alterView and alterSequence to call uni-task methods (alter user, comment, schema, ...) from the public one instead of having all the code in the main method. - php5 migration: lib.inc.php require at least php 5.0
-rwxr-xr-xclasses/database/Connection.php40
-rwxr-xr-xclasses/database/Postgres.php7187
-rw-r--r--classes/database/Postgres71.php511
-rw-r--r--classes/database/Postgres72.php688
-rw-r--r--classes/database/Postgres73.php2509
-rw-r--r--classes/database/Postgres74.php939
-rw-r--r--classes/database/Postgres80.php623
-rw-r--r--classes/database/Postgres81.php672
-rw-r--r--classes/database/Postgres82.php375
-rw-r--r--classes/database/Postgres83.php798
-rw-r--r--libraries/lib.inc.php2
-rw-r--r--selenium/tests/config.inc.php2
12 files changed, 5764 insertions, 8582 deletions
diff --git a/classes/database/Connection.php b/classes/database/Connection.php
index 935e979e..a0a00dff 100755
--- a/classes/database/Connection.php
+++ b/classes/database/Connection.php
@@ -79,29 +79,25 @@ class Connection {
$description = "PostgreSQL {$version}";
// Detect version and choose appropriate database driver
- // If unknown version, then default to latest driver
- // All 6.x versions default to oldest driver, even though
- // it won't work with those versions.
- if ((int)substr($version, 0, 1) < 7)
+ switch (substr($version,0,3)) {
+ case '8.3': return 'Postgres'; break;
+ case '8.2': return 'Postgres82'; break;
+ case '8.1': return 'Postgres81'; break;
+ case '8.0':
+ case '7.5': return 'Postgres80'; break;
+ case '7.4': return 'Postgres74'; break;
+ case '7.3': return 'Postgres73'; break;
+ }
+
+ /* All <7.3 versions are not supported */
+ // if major version is 7 or less and wasn't catch in the
+ // switch/case block, we have an unsupported version.
+ if ((int)substr($version, 0, 1) < 8)
return null;
- elseif (strpos($version, '8.2') === 0)
- return 'Postgres82';
- elseif (strpos($version, '8.1') === 0)
- return 'Postgres81';
- elseif (strpos($version, '8.0') === 0 || strpos($version, '7.5') === 0)
- return 'Postgres80';
- elseif (strpos($version, '7.4') === 0)
- return 'Postgres74';
- elseif (strpos($version, '7.3') === 0)
- return 'Postgres73';
- elseif (strpos($version, '7.2') === 0)
- return 'Postgres72';
- elseif (strpos($version, '7.1') === 0)
- return 'Postgres71';
- elseif (strpos($version, '7.0') === 0)
- return 'Postgres';
- else
- return 'Postgres83';
+
+ // If unknown version, then default to latest driver
+ return 'Postgres';
+
}
/**
diff --git a/classes/database/Postgres.php b/classes/database/Postgres.php
index 51b6ff73..58e481df 100755
--- a/classes/database/Postgres.php
+++ b/classes/database/Postgres.php
@@ -1,66 +1,32 @@
<?php
/**
- * A class that implements the DB interface for Postgres
- * Note: This class uses ADODB and returns RecordSets.
+ * A Class that implements the DB Interface for Postgres
+ * Note: This Class uses ADODB and returns RecordSets.
*
* $Id: Postgres.php,v 1.320 2008/02/20 20:43:09 ioguix Exp $
*/
-// @@@ THOUGHT: What about inherits? ie. use of ONLY???
-
include_once('./classes/database/ADODB_base.php');
class Postgres extends ADODB_base {
- // Major version. MUST be numerically comparable to other versions.
- var $major_version = 7.0;
- // Array of allowed type alignments
- var $typAligns = array('char', 'int2', 'int4', 'double');
- // The default type alignment
- var $typAlignDef = 'int4';
- // Array of allowed type storage attributes
- var $typStorages = array('plain', 'external', 'extended', 'main');
- // The default type storage
- var $typStorageDef = 'plain';
- // Extra "magic" types
- var $extraTypes = array('SERIAL');
- // Array of allowed index types
- var $typIndexes = array('BTREE', 'RTREE', 'GIST', 'HASH');
- // Default index type
- var $typIndexDef = 'BTREE';
- // Array of allowed trigger events
- var $triggerEvents= array('INSERT', 'UPDATE', 'DELETE', 'INSERT OR UPDATE', 'INSERT OR DELETE',
- 'DELETE OR UPDATE', 'INSERT OR DELETE OR UPDATE');
- // When to execute the trigger
- var $triggerExecTimes = array('BEFORE', 'AFTER');
- // How often to execute the trigger
- var $triggerFrequency = array('ROW');
- // Foreign key stuff. First element MUST be the default.
- var $fkactions = array('NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT');
- var $fkmatches = array('MATCH SIMPLE', 'MATCH FULL');
- var $fkdeferrable = array('NOT DEFERRABLE', 'DEFERRABLE');
- var $fkinitial = array('INITIALLY IMMEDIATE', 'INITIALLY DEFERRED');
- // Function properties
- var $funcprops = array(array('', 'ISCACHABLE'));
- var $defaultprops = array('');
-
- // Last oid assigned to a system object
- var $_lastSystemOID = 18539;
- var $_maxNameLen = 31;
-
- // Name of id column
- var $id = 'oid';
-
+ var $major_version = 8.3;
+ // Max object name length
+ var $_maxNameLen = 63;
+ // Store the current schema
+ var $_schema;
// Map of database encoding names to HTTP encoding names. If a
// database encoding does not appear in this list, then its HTTP
// encoding name is the same as its database encoding name.
var $codemap = array(
- 'ALT' => 'CP866',
+ 'BIG5' => 'BIG5',
'EUC_CN' => 'GB2312',
'EUC_JP' => 'EUC-JP',
'EUC_KR' => 'EUC-KR',
'EUC_TW' => 'EUC-TW',
+ 'GB18030' => 'GB18030',
+ 'GBK' => 'GB2312',
'ISO_8859_5' => 'ISO-8859-5',
'ISO_8859_6' => 'ISO-8859-6',
'ISO_8859_7' => 'ISO-8859-7',
@@ -71,22 +37,44 @@ class Postgres extends ADODB_base {
'LATIN2' => 'ISO-8859-2',
'LATIN3' => 'ISO-8859-3',
'LATIN4' => 'ISO-8859-4',
- // The following encoding map is a known error in PostgreSQL < 7.2
- // See the constructor for Postgres72.
- 'LATIN5' => 'ISO-8859-5',
+ 'LATIN5' => 'ISO-8859-9',
'LATIN6' => 'ISO-8859-10',
'LATIN7' => 'ISO-8859-13',
'LATIN8' => 'ISO-8859-14',
'LATIN9' => 'ISO-8859-15',
'LATIN10' => 'ISO-8859-16',
+ 'SJIS' => 'SHIFT_JIS',
'SQL_ASCII' => 'US-ASCII',
- 'TCVN' => 'CP1258',
- 'UNICODE' => 'UTF-8',
- 'WIN' => 'CP1251',
+ 'UHC' => 'WIN949',
+ 'UTF8' => 'UTF-8',
+ 'WIN866' => 'CP866',
'WIN874' => 'CP874',
- 'WIN1256' => 'CP1256'
+ 'WIN1250' => 'CP1250',
+ 'WIN1251' => 'CP1251',
+ 'WIN1252' => 'CP1252',
+ 'WIN1256' => 'CP1256',
+ 'WIN1258' => 'CP1258'
);
-
+ var $defaultprops = array('', '', '');
+ // Extra "magic" types. BIGSERIAL was added in PostgreSQL 7.2.
+ var $extraTypes = array('SERIAL', 'BIGSERIAL');
+ // Foreign key stuff. First element MUST be the default.
+ var $fkactions = array('NO ACTION', 'RESTRICT', 'CASCADE', 'SET NULL', 'SET DEFAULT');
+ var $fkdeferrable = array('NOT DEFERRABLE', 'DEFERRABLE');
+ var $fkinitial = array('INITIALLY IMMEDIATE', 'INITIALLY DEFERRED');
+ var $fkmatches = array('MATCH SIMPLE', 'MATCH FULL');
+ // Function properties
+ var $funcprops = array( array('', 'VOLATILE', 'IMMUTABLE', 'STABLE'),
+ array('', 'CALLED ON NULL INPUT', 'RETURNS NULL ON NULL INPUT'),
+ array('', 'SECURITY INVOKER', 'SECURITY DEFINER'));
+ // Default help URL
+ var $help_base;
+ // Help sub pages
+ var $help_page;
+ // Name of id column
+ var $id = 'oid';
+ // Supported join operations for use with view wizard
+ var $joinOps = array('INNER JOIN' => 'INNER JOIN', 'LEFT JOIN' => 'LEFT JOIN', 'RIGHT JOIN' => 'RIGHT JOIN', 'FULL JOIN' => 'FULL JOIN');
// Map of internal language name to syntax highlighting name
var $langmap = array(
'sql' => 'SQL',
@@ -114,45 +102,66 @@ class Postgres extends ADODB_base {
'plruby' => 'Ruby',
'plrubyu' => 'Ruby'
);
-
+ // Predefined size types
+ var $predefined_size_types = array('abstime','aclitem','bigserial','boolean','bytea','cid','cidr','circle','date','float4','float8','gtsvector','inet','int2','int4','int8','macaddr','money','oid','path','polygon','refcursor','regclass','regoper','regoperator','regproc','regprocedure','regtype','reltime','serial','smgr','text','tid','tinterval','tsquery','tsvector','varbit','void','xid');
// List of all legal privileges that can be applied to different types
// of objects.
var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'RULE', 'ALL'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'RULE', 'ALL'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL')
+ 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
+ 'database' => array('CREATE', 'TEMPORARY', 'CONNECT', 'ALL PRIVILEGES'),
+ 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
+ 'language' => array('USAGE', 'ALL PRIVILEGES'),
+ 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES'),
+ 'tablespace' => array('CREATE', 'ALL PRIVILEGES')
);
-
// List of characters in acl lists and the privileges they
// refer to.
var $privmap = array(
'r' => 'SELECT',
'w' => 'UPDATE',
'a' => 'INSERT',
- 'R' => 'RULE'
+ 'd' => 'DELETE',
+ 'R' => 'RULE',
+ 'x' => 'REFERENCES',
+ 't' => 'TRIGGER',
+ 'X' => 'EXECUTE',
+ 'U' => 'USAGE',
+ 'C' => 'CREATE',
+ 'T' => 'TEMPORARY',
+ 'c' => 'CONNECT'
);
-
// Rule action types
var $rule_events = array('SELECT', 'INSERT', 'UPDATE', 'DELETE');
-
// Select operators
- // Operators of type 'i' are 'infix', eg. a = '1'. Type 'p' means postfix unary, eg. a IS TRUE.
- // 'x' is a bracketed subquery form. eg. IN (1,2,3)
- var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
- 'LIKE' => 'i', 'NOT LIKE' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
- 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x');
-
- // Supported join operations for use with view wizard
- var $joinOps = array('INNER JOIN' => 'INNER JOIN');
-
- // Default help URL
- var $help_base;
-
- // Help sub pages
- var $help_page;
-
- // Predefined size types
- var $predefined_size_types = array('abstime','aclitem','bigserial','boolean','bytea','cid','cidr','circle','date','float4','float8','gtsvector','inet','int2','int4','int8','macaddr','money','oid','path','polygon','refcursor','regclass','regoper','regoperator','regproc','regprocedure','regtype','reltime','serial','smgr','text','tid','tinterval','tsquery','tsvector','varbit','void','xid');
+ var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i',
+ '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
+ 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', 'SIMILAR TO' => 'i',
+ 'NOT SIMILAR TO' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
+ 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x',
+ '@@' => 'i', '@@@' => 'i', '@>' => 'i', '<@' => 'i',
+ '@@ to_tsquery' => 't', '@@@ to_tsquery' => 't', '@> to_tsquery' => 't', '<@ to_tsquery' => 't',
+ '@@ plainto_tsquery' => 't', '@@@ plainto_tsquery' => 't', '@> plainto_tsquery' => 't', '<@ plainto_tsquery' => 't');
+ // Array of allowed trigger events
+ var $triggerEvents= array('INSERT', 'UPDATE', 'DELETE', 'INSERT OR UPDATE', 'INSERT OR DELETE',
+ 'DELETE OR UPDATE', 'INSERT OR DELETE OR UPDATE');
+ // When to execute the trigger
+ var $triggerExecTimes = array('BEFORE', 'AFTER');
+ // How often to execute the trigger
+ var $triggerFrequency = array('ROW','STATEMENT');
+ // Array of allowed type alignments
+ var $typAligns = array('char', 'int2', 'int4', 'double');
+ // The default type alignment
+ var $typAlignDef = 'int4';
+ // Default index type
+ var $typIndexDef = 'BTREE';
+ // Array of allowed index types
+ var $typIndexes = array('BTREE', 'RTREE', 'GIST', 'GIN', 'HASH');
+ // Array of allowed type storage attributes
+ var $typStorages = array('plain', 'external', 'extended', 'main');
+ // The default type storage
+ var $typStorageDef = 'plain';
/**
* Constructor
@@ -162,35 +171,6 @@ class Postgres extends ADODB_base {
$this->ADODB_base($conn);
}
- // Help functions
-
- /**
- * Fetch a URL (or array of URLs) for a given help page.
- */
- function getHelp($help) {
- $this->getHelpPages();
-
- if (isset($this->help_page[$help])) {
- if (is_array($this->help_page[$help])) {
- $urls = array();
- foreach ($this->help_page[$help] as $link) {
- $urls[] = $this->help_base . $link;
- }
- return $urls;
- } else
- return $this->help_base . $this->help_page[$help];
- } else
- return null;
- }
-
- /**
- * Initialize help pages and return the full list
- */
- function getHelpPages() {
- include_once('./help/PostgresDoc70.php');
- return $this->help_page;
- }
-
// Formatting functions
/**
@@ -220,30 +200,30 @@ class Postgres extends ADODB_base {
}
/**
- * Cleans (escapes) an array
+ * Cleans (escapes) an array of field names
* @param $arr The array to clean, by reference
* @return The cleaned array
*/
- function arrayClean(&$arr) {
+ function fieldArrayClean(&$arr) {
foreach ($arr as $k => $v) {
if ($v === null) continue;
- if (function_exists('pg_escape_string'))
- $arr[$k] = pg_escape_string($v);
- else
- $arr[$k] = addslashes($v);
+ $arr[$k] = str_replace('"', '""', $v);
}
return $arr;
}
/**
- * Cleans (escapes) an array of field names
+ * Cleans (escapes) an array
* @param $arr The array to clean, by reference
* @return The cleaned array
*/
- function fieldArrayClean(&$arr) {
+ function arrayClean(&$arr) {
foreach ($arr as $k => $v) {
if ($v === null) continue;
- $arr[$k] = str_replace('"', '""', $v);
+ if (function_exists('pg_escape_string'))
+ $arr[$k] = pg_escape_string($v);
+ else
+ $arr[$k] = addslashes($v);
}
return $arr;
}
@@ -422,16 +402,46 @@ class Postgres extends ADODB_base {
return $temp;
}
+ // Help functions
+
/**
- * Returns the current schema to prepend on object names
+ * Fetch a URL (or array of URLs) for a given help page.
*/
- function schema() {
- return '';
+ function getHelp($help) {
+ $this->getHelpPages();
+
+ if (isset($this->help_page[$help])) {
+ if (is_array($this->help_page[$help])) {
+ $urls = array();
+ foreach ($this->help_page[$help] as $link) {
+ $urls[] = $this->help_base . $link;
+ }
+ return $urls;
+ } else
+ return $this->help_base . $this->help_page[$help];
+ } else
+ return null;
+ }
+
+ function getHelpPages() {
+ include_once('./help/PostgresDoc83.php');
+ return $this->help_page;
}
// Database functions
/**
+ * Return all information about a particular database
+ * @param $database The name of the database to retrieve
+ * @return The database info
+ */
+ function getDatabase($database) {
+ $this->clean($database);
+ $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
+ return $this->selectSet($sql);
+ }
+
+ /**
* Return all database available on the server
* @return A list of databases, sorted alphabetically
*/
@@ -443,7 +453,7 @@ class Postgres extends ADODB_base {
if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
$username = $server_info['username'];
$this->clean($username);
- $clause = " AND pu.usename='{$username}'";
+ $clause = " AND pr.rolname='{$username}'";
}
else $clause = '';
@@ -453,14 +463,16 @@ class Postgres extends ADODB_base {
$orderby = "ORDER BY pdb.datname";
if (!$conf['show_system'])
- $where = "AND pdb.datname NOT IN ('template1')";
+ $where = ' AND NOT pdb.datistemplate';
else
- $where = '';
-
- $sql = "SELECT pdb.datname, pu.usename AS datowner, pg_encoding_to_char(encoding) AS datencoding,
- (SELECT description FROM pg_description pd WHERE pdb.oid=pd.objoid) AS datcomment
- FROM pg_database pdb, pg_user pu
- WHERE pdb.datdba = pu.usesysid
+ $where = ' AND pdb.datallowconn';
+
+ $sql = "SELECT pdb.datname AS datname, pr.rolname AS datowner, pg_encoding_to_char(encoding) AS datencoding,
+ (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid) AS datcomment,
+ (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace,
+ pg_catalog.pg_database_size(pdb.oid) as dbsize
+ FROM pg_catalog.pg_database pdb LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
+ WHERE true
{$where}
{$clause}
{$orderby}";
@@ -469,24 +481,24 @@ class Postgres extends ADODB_base {
}
/**
- * Return the database owner of a db
- * @param string $database the name of the database to get the owner for
- * @return recordset of the db owner info
+ * Return the database comment of a db from the shared description table
+ * @param string $database the name of the database to get the comment for
+ * @return recordset of the db comment info
*/
- function getDatabaseOwner($database) {
+ function getDatabaseComment($database) {
$this->clean($database);
- $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
+ $sql = "SELECT description FROM pg_catalog.pg_database JOIN pg_catalog.pg_shdescription ON (oid=objoid) WHERE pg_database.datname = '{$database}' ";
return $this->selectSet($sql);
}
/**
- * Return all information about a particular database
- * @param $database The name of the database to retrieve
- * @return The database info
+ * Return the database owner of a db
+ * @param string $database the name of the database to get the owner for
+ * @return recordset of the db owner info
*/
- function getDatabase($database) {
+ function getDatabaseOwner($database) {
$this->clean($database);
- $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
+ $sql = "SELECT usename FROM pg_user, pg_database WHERE pg_user.usesysid = pg_database.datdba AND pg_database.datname = '{$database}' ";
return $this->selectSet($sql);
}
@@ -507,12 +519,19 @@ class Postgres extends ADODB_base {
}
/**
- * Sets the client encoding
- * @param $encoding The encoding to for the client
- * @return 0 success
+ * Returns the current default_with_oids setting
+ * @return default_with_oids setting
*/
- function setClientEncoding($encoding) {
- return -99;
+ function getDefaultWithOid() {
+ // Try to avoid a query if at all possible (5)
+ if (function_exists('pg_parameter_status')) {
+ $default = pg_parameter_status($this->conn->_connectionID, 'default_with_oids');
+ if ($default !== false) return $default;
+ }
+
+ $sql = "SHOW default_with_oids";
+
+ return $this->selectField($sql, 'default_with_oids');
}
/**
@@ -547,7 +566,25 @@ class Postgres extends ADODB_base {
}
return 0;
+ }
+ /**
+ * Renames a database, note that this operation cannot be
+ * performed on a database that is currently being connected to
+ * @param string $oldName name of database to rename
+ * @param string $newName new name of database
+ * @return int 0 on success
+ */
+ function alterDatabaseRename($oldName, $newName) {
+ $this->clean($oldName);
+ $this->clean($newName);
+
+ if ($oldName != $newName) {
+ $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\"";
+ return $this->execute($sql);
+ }
+ else //just return success, we're not going to do anything
+ return 0;
}
/**
@@ -561,419 +598,617 @@ class Postgres extends ADODB_base {
return $this->execute($sql);
}
- // Schema functions
-
/**
- * Sets the current working schema. This is a do nothing method for
- * < 7.3 and is just here for polymorphism's sake.
- * @param $schema The the name of the schema to work in
- * @return 0 success
+ * Changes ownership of a database
+ * This can only be done by a superuser or the owner of the database
+ * @param string $dbName database to change ownership of
+ * @param string $newOwner user that will own the database
+ * @return int 0 on success
*/
- function setSchema($schema) {
- return 0;
- }
+ function alterDatabaseOwner($dbName, $newOwner) {
+ $this->clean($dbName);
+ $this->clean($newOwner);
- // Inheritance functions
+ $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
+ return $this->execute($sql);
+ }
/**
- * Finds the names and schemas of parent tables (in order)
- * @param $table The table to find the parents for
- * @return A recordset
+ * Alters a database
+ * the multiple return vals are for postgres 8+ which support more functionality in alter database
+ * @param $dbName The name of the database
+ * @param $newName new name for the database
+ * @param $newOwner The new owner for the database
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 owner error
+ * @return -3 rename error
+ * @return -4 comment error
*/
- function getTableParents($table) {
- $this->clean($table);
+ function alterDatabase($dbName, $newName, $newOwner = '', $comment = '') {
+ $this->clean($dbName);
+ $this->clean($newName);
+ $this->clean($newOwner);
+ $this->clean($comment);
- $sql = "
- SELECT
- NULL AS nspname, relname
- FROM
- pg_class pc, pg_inherits pi
- WHERE
- pc.oid=pi.inhparent
- AND pi.inhrelid = (SELECT oid from pg_class WHERE relname='{$table}')
- ORDER BY
- pi.inhseqno
- ";
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
- return $this->selectSet($sql);
+ if ($dbName != $newName) {
+ $status = $this->alterDatabaseRename($dbName, $newName);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+ }
+
+ $status = $this->alterDatabaseOwner($newName, $newOwner);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
}
+ if (trim($comment) != '' ) {
+ $status = $this->setComment('DATABASE', $dbName, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
+ }
+ }
+ return $this->endTransaction();
+ }
/**
- * Finds the names and schemas of child tables
- * @param $table The table to find the children for
+ * Returns prepared transactions information
+ * @param $database (optional) Find only prepared transactions executed in a specific database
* @return A recordset
*/
- function getTableChildren($table) {
- $this->clean($table);
-
- $sql = "
- SELECT
- NULL AS nspname, relname
- FROM
- pg_class pc, pg_inherits pi
- WHERE
- pc.oid=pi.inhrelid
- AND pi.inhparent = (SELECT oid from pg_class WHERE relname='{$table}')
- ";
+ function getPreparedXacts($database = null) {
+ if ($database === null)
+ $sql = "SELECT * FROM pg_prepared_xacts";
+ else {
+ $this->clean($database);
+ $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts
+ WHERE database='{$database}' ORDER BY owner";
+ }
return $this->selectSet($sql);
}
- // Table functions
-
/**
- * Returns table information
- * @param $table The name of the table
+ * Searches all system catalogs to find objects that match a certain name.
+ * @param $term The search term
+ * @param $filter The object type to restrict to ('' means no restriction)
* @return A recordset
*/
- function getTable($table) {
- $this->clean($table);
+ function findObject($term, $filter) {
+ global $conf;
- $sql = "SELECT pc.relname,
- pg_get_userbyid(pc.relowner) AS relowner,
- (SELECT description FROM pg_description pd
- WHERE pc.oid=pd.objoid) AS relcomment
- FROM pg_class pc
- WHERE pc.relname='{$table}'";
+ // Escape search term for ILIKE match
+ $term = str_replace('_', '\\_', $term);
+ $term = str_replace('%', '\\%', $term);
+ $this->clean($term);
+ $this->clean($filter);
- return $this->selectSet($sql);
+ // Exclude system relations if necessary
+ if (!$conf['show_system']) {
+ // XXX: The mention of information_schema here is in the wrong place, but
+ // it's the quickest fix to exclude the info schema from 7.4
+ $where = " AND pn.nspname NOT LIKE 'pg\\\\_%' AND pn.nspname != 'information_schema'";
+ $lan_where = "AND pl.lanispl";
+ }
+ else {
+ $where = '';
+ $lan_where = '';
}
- /**
- * Return all tables in current database
- * @param $all True to fetch all tables, false for just in current schema
- * @return All tables, sorted alphabetically
- */
- function getTables($all = false) {
- global $conf;
- if (!$conf['show_system'] || $all) $where = "AND c.relname NOT LIKE 'pg@_%' ESCAPE '@' ";
- else $where = '';
-
- $sql = "SELECT NULL AS nspname, c.relname,
- (SELECT usename FROM pg_user u WHERE u.usesysid=c.relowner) AS relowner,
- (SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS relcomment,
- reltuples::bigint AS reltuples
- FROM pg_class c
- WHERE c.relkind='r'
- AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
+ // Apply outer filter
+ $sql = '';
+ if ($filter != '') {
+ $sql = "SELECT * FROM (";
+ }
+
+ $sql .= "
+ SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name
+ FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
+ pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
+ WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ 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,
+ pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid
+ AND pa.attname ILIKE '%{$term}%' AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
+ UNION ALL
+ 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
+ WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
+ pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid
+ AND pi.indexrelid=pc2.oid
+ AND NOT EXISTS (
+ SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
+ )
+ AND pc2.relname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
+ pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
+ AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
+ SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
+ ) END
+ AND pc2.conname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
+ pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
+ AND pc.conname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
+ pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
+ AND (NOT pt.tgisconstraint OR NOT EXISTS
+ (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
+ AND pt.tgname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
+ JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
+ LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
+ WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
+ JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
+ LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
+ WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
+ ";
+
+ // Add advanced objects if show_advanced is set
+ if ($conf['show_advanced']) {
+ $sql .= "
+ UNION ALL
+ SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL,
+ pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
+ WHERE pt.typnamespace=pn.oid AND typname ILIKE '%{$term}%'
+ AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
{$where}
- ORDER BY relname";
+ UNION ALL
+ SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn
+ WHERE po.oprnamespace=pn.oid AND oprname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
+ pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
+ WHERE lanname ILIKE '%{$term}%' {$lan_where}
+ UNION ALL
+ SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
+ WHERE p.proisagg AND p.proname ILIKE '%{$term}%' {$where}
+ UNION ALL
+ SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
+ pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
+ AND po.opcname ILIKE '%{$term}%' {$where}
+ ";
+ }
+ // Otherwise just add domains
+ else {
+ $sql .= "
+ UNION ALL
+ SELECT 'DOMAIN', pt.oid, pn.nspname, NULL,
+ pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
+ WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE '%{$term}%'
+ AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
+ {$where}
+ ";
+ }
+
+ if ($filter != '') {
+ // We use like to make RULE, CONSTRAINT and COLUMN searches work
+ $sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
+ }
+
+ $sql .= "ORDER BY type, schemaname, relname, name";
+
return $this->selectSet($sql);
}
/**
- * Retrieve the attribute definition of a table
- * @param $table The name of the table
- * @param $field (optional) The name of a field to return
- * @return All attributes in order
+ * Returns the specified variable information.
+ * @return the field
*/
- function getTableAttributes($table, $field = '') {
- $this->clean($table);
- $this->clean($field);
-
- if ($field == '') {
- $sql = "SELECT
- a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
- (SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
- a.attstorage AS typstorage, false AS attisserial,
- (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
- FROM
- pg_attribute a,
- pg_class c,
- pg_type t
- WHERE
- c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
- ORDER BY a.attnum";
- }
- else {
- $sql = "SELECT
- a.attname, t.typname as type, t.typname as base_type,
- a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
- (SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
- a.attstorage AS typstorage,
- (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
- FROM
- pg_attribute a ,
- pg_class c,
- pg_type t
- WHERE
- c.relname = '{$table}' AND a.attname='{$field}' AND a.attrelid = c.oid AND a.atttypid = t.oid";
- }
+ function getVariable($setting) {
+ $sql = "SHOW $setting";
return $this->selectSet($sql);
}
/**
- * Checks to see whether or not a table has a unique id column
- * @param $table The table name
- * @return True if it has a unique id, false otherwise
- * @return -99 error
+ * Returns all available variable information.
+ * @return A recordset
*/
- function hasObjectID($table) {
- // 7.0 and 7.1 always had an oid column
- return true;
+ function getVariables() {
+ $sql = "SHOW ALL";
+
+ return $this->selectSet($sql);
}
+ // Schema functons
+
/**
- * Returns the current default_with_oids setting
- * @return default_with_oids setting
+ * Returns the current schema to prepend on object names
*/
- function getDefaultWithOid() {
- // 8.0 is the first release to have this setting
- // Prior releases don't have this setting... oids always activated
- return 'on';
+ function schema() {
+ return "\"{$this->_schema}\".";
}
/**
- * Creates a new table in the database
- * @param $name The name of the table
- * @param $fields The number of fields
- * @param $field An array of field names
- * @param $type An array of field types
- * @param $array An array of '' or '[]' for each type if it's an array or not
- * @param $length An array of field lengths
- * @param $notnull An array of not null
- * @param $default An array of default values
- * @param $withoutoids True if WITHOUT OIDS, false otherwise
- * @param $colcomment An array of comments
- * @param $comment Table comment
- * @param $tablespace The tablespace name ('' means none/default)
- * @param $uniquekey An Array indicating the fields that are unique (those indexes that are set)
- * @param $primarykey An Array indicating the field used for the primarykey (those indexes that are set)
- * @return 0 success
- * @return -1 no fields supplied
+ * Return all schemas in the current database. This differs from the version
+ * in 7.3 only in that it considers the information_schema to be a system schema.
+ * @return All schemas, sorted alphabetically
*/
- function createTable($name, $fields, $field, $type, $array, $length, $notnull,
- $default, $withoutoids, $colcomment, $tblcomment, $tablespace,
- $uniquekey, $primarykey) {
- $this->fieldClean($name);
- $this->clean($tblcomment);
+ function getSchemas() {
+ global $conf, $slony;
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
+ if (!$conf['show_system']) {
+ $where = "WHERE nspname NOT LIKE 'pg@_%' ESCAPE '@' AND nspname != 'information_schema'";
+ if (isset($slony) && $slony->isEnabled()) {
+ $temp = $slony->slony_schema;
+ $this->clean($temp);
+ $where .= " AND nspname != '{$temp}'";
+ }
- $schema = $this->schema();
+ }
+ else $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
+ $sql = "
+ SELECT pn.nspname, pu.usename AS nspowner,
+ pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
+ FROM pg_catalog.pg_namespace pn
+ LEFT JOIN pg_catalog.pg_user pu ON (pn.nspowner = pu.usesysid)
+ {$where}
+ ORDER BY nspname";
- $found = false;
- $first = true;
- $comment_sql = ''; //Accumulate comments for the columns
- $sql = "CREATE TABLE {$schema}\"{$name}\" (";
- for ($i = 0; $i < $fields; $i++) {
- $this->fieldClean($field[$i]);
- $this->clean($type[$i]);
- $this->clean($length[$i]);
- $this->clean($colcomment[$i]);
+ return $this->selectSet($sql);
+ }
- // Skip blank columns - for user convenience
- if ($field[$i] == '' || $type[$i] == '') continue;
- // If not the first column, add a comma
- if (!$first) $sql .= ", ";
- else $first = false;
+ /**
+ * Return all information relating to a schema
+ * @param $schema The name of the schema
+ * @return Schema information
+ */
+ function getSchemaByName($schema) {
+ $this->clean($schema);
+ $sql = "
+ SELECT nspname, nspowner, nspacl,
+ pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
+ FROM pg_catalog.pg_namespace pn
+ WHERE nspname='{$schema}'";
+ return $this->selectSet($sql);
+ }
- switch ($type[$i]) {
- // Have to account for weird placing of length for with/without
- // time zone types
- case 'timestamp with time zone':
- case 'timestamp without time zone':
- $qual = substr($type[$i], 9);
- $sql .= "\"{$field[$i]}\" timestamp";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
- $sql .= $qual;
- break;
- case 'time with time zone':
- case 'time without time zone':
- $qual = substr($type[$i], 4);
- $sql .= "\"{$field[$i]}\" time";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
- $sql .= $qual;
- break;
- default:
- $sql .= "\"{$field[$i]}\" {$type[$i]}";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
+ /**
+ * Sets the current working schema. Will also set Class variable.
+ * @param $schema The the name of the schema to work in
+ * @return 0 success
+ */
+ function setSchema($schema) {
+ // Get the current schema search path, including 'pg_catalog'.
+ $search_path = $this->getSearchPath();
+ // Prepend $schema to search path
+ array_unshift($search_path, $schema);
+ $status = $this->setSearchPath($search_path);
+ if ($status == 0) {
+ $this->clean($schema);
+ $this->_schema = $schema;
+ return 0;
}
- // Add array qualifier if necessary
- if ($array[$i] == '[]') $sql .= '[]';
- // Add other qualifiers
- if (!isset($primarykey[$i])) {
- if (isset($uniquekey[$i])) $sql .= " UNIQUE";
- if (isset($notnull[$i])) $sql .= " NOT NULL";
+ else return $status;
}
- if ($default[$i] != '') $sql .= " DEFAULT {$default[$i]}";
- if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
+ /**
+ * Sets the current schema search path
+ * @param $paths An array of schemas in required search order
+ * @return 0 success
+ * @return -1 Array not passed
+ * @return -2 Array must contain at least one item
+ */
+ function setSearchPath($paths) {
+ if (!is_array($paths)) return -1;
+ elseif (sizeof($paths) == 0) return -2;
+ elseif (sizeof($paths) == 1 && $paths[0] == '') {
+ // Need to handle empty paths in some cases
+ $paths[0] = 'pg_catalog';
+ }
- $found = true;
+ // Loop over all the paths to check that none are empty
+ $temp = array();
+ foreach ($paths as $schema) {
+ if ($schema != '') $temp[] = $schema;
}
+ $this->fieldArrayClean($temp);
- if (!$found) return -1;
+ $sql = 'SET SEARCH_PATH TO "' . implode('","', $temp) . '"';
- // PRIMARY KEY
- $primarykeycolumns = array();
- for ($i = 0; $i < $fields; $i++) {
- if (isset($primarykey[$i])) {
- $primarykeycolumns[] = "\"{$field[$i]}\"";
- }
- }
- if (count($primarykeycolumns) > 0) {
- $sql .= ", PRIMARY KEY (" . implode(", ", $primarykeycolumns) . ")";
+ return $this->execute($sql);
}
- $sql .= ")";
+ /**
+ * Creates a new schema.
+ * @param $schemaname The name of the schema to create
+ * @param $authorization (optional) The username to create the schema for.
+ * @param $comment (optional) If omitted, defaults to nothing
+ * @return 0 success
+ */
+ function createSchema($schemaname, $authorization = '', $comment = '') {
+ $this->fieldClean($schemaname);
+ $this->fieldClean($authorization);
+ $this->clean($comment);
- // WITHOUT OIDS
- if ($this->hasWithoutOIDs() && $withoutoids)
- $sql .= ' WITHOUT OIDS';
+ $sql = "CREATE SCHEMA \"{$schemaname}\"";
+ if ($authorization != '') $sql .= " AUTHORIZATION \"{$authorization}\"";
- // Tablespace
- if ($this->hasTablespaces() && $tablespace != '') {
- $this->fieldClean($tablespace);
- $sql .= " TABLESPACE \"{$tablespace}\"";
+ if ($comment != '') {
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
}
+ // Create the new schema
$status = $this->execute($sql);
- if ($status) {
+ if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
- if ($tblcomment != '') {
- $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
- if ($status) {
+ // Set the comment
+ if ($comment != '') {
+ $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
+ if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
- }
- if ($comment_sql != '') {
- $status = $this->execute($comment_sql);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
- }
return $this->endTransaction();
+ }
+ return 0;
}
/**
- * Protected method which alter a table
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $tblrs The table recordSet returned by getTable()
- * @param $name The new name for the table
- * @param $owner The new owner for the table
- * @param $schema The new schema for the table
- * @param $comment The comment on the table
- * @param $tablespace The new tablespace for the table ('' means leave as is)
+ * Updates a schema.
+ * @param $schemaname The name of the schema to drop
+ * @param $comment The new comment for this schema
* @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 tablespace error
- * @return -7 schema error
*/
- /* protected */
- function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
-
+ function updateSchema($schemaname, $comment, $name) {
+ $this->fieldClean($schemaname);
$this->fieldClean($name);
$this->clean($comment);
- /* $schema, $owner, $tablespace not supported in pg70 */
- $table = $tblrs->fields['relname'];
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
- // Comment
- $status = $this->setComment('TABLE', '', $table, $comment);
- if ($status != 0) return -4;
+ $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
- // Rename (only if name has changed)
- if ($name != $table) {
- $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$name}\"";
+ // Only if the name has changed
+ if ($name != $schemaname) {
+ $sql = "ALTER SCHEMA \"{$schemaname}\" RENAME TO \"{$name}\"";
$status = $this->execute($sql);
- if ($status != 0) return -3;
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
}
- return 0;
+ return $this->endTransaction();
}
/**
- * Alter table properties
- * @param $table The name of the table
- * @param $name The new name for the table
- * @param $owner The new owner for the table
- * @param $schema The new schema for the table
- * @param $comment The comment on the table
- * @param $tablespace The new tablespace for the table ('' means leave as is)
+ * Drops a schema.
+ * @param $schemaname The name of the schema to drop
+ * @param $cascade True to cascade drop, false to restrict
* @return 0 success
- * @return -1 transaction error
- * @return -2 get existing table error
- * @return $this->_alterTable error code
*/
- function alterTable($table, $name, $owner, $schema, $comment, $tablespace) {
+ function dropSchema($schemaname, $cascade) {
+ $this->fieldClean($schemaname);
- $this->fieldClean($table);
- $data = $this->getTable($table);
- if ($data->recordCount() != 1)
- return -2;
+ $sql = "DROP SCHEMA \"{$schemaname}\"";
+ if ($cascade) $sql .= " CASCADE";
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
+ return $this->execute($sql);
}
- $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
+ /**
+ * Return the current schema search path
+ * @return Array of schema names
+ */
+ function getSearchPath() {
+ $sql = 'SELECT current_schemas(false) AS search_path';
- if ($status != 0) {
- $this->rollbackTransaction();
- return $status;
+ return $this->phpArray($this->selectField($sql, 'search_path'));
}
- return $this->endTransaction();
+ // Table functions
+
+ /**
+ * Returns table information
+ * @param $table The name of the table
+ * @return A recordset
+ */
+ function getTable($table) {
+ $this->clean($table);
+
+ $sql = "
+ SELECT
+ c.relname, n.nspname, u.usename AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
+ (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind = 'r'
+ AND n.nspname = '{$this->_schema}'
+ AND n.oid = c.relnamespace
+ AND c.relname = '{$table}'";
+
+ return $this->selectSet($sql);
}
/**
- * Removes a table from the database
- * @param $table The table to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
+ * Return all tables in current database (and schema)
+ * @param $all True to fetch all tables, false for just in current schema
+ * @return All tables, sorted alphabetically
*/
- function dropTable($table, $cascade) {
- $this->fieldClean($table);
+ function getTables($all = false) {
+ if ($all) {
+ // Exclude pg_catalog and information_schema tables
+ $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
+ FROM pg_catalog.pg_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
+ ORDER BY schemaname, tablename";
+ } else {
+ $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
+ reltuples::bigint,
+ (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind = 'r'
+ AND nspname='{$this->_schema}'
+ ORDER BY c.relname";
+ }
- $sql = "DROP TABLE \"{$table}\"";
- if ($cascade) $sql .= " CASCADE";
+ return $this->selectSet($sql);
+ }
- return $this->execute($sql);
+ /**
+ * Retrieve the attribute definition of a table
+ * @param $table The name of the table
+ * @param $field (optional) The name of a field to return
+ * @return All attributes in order
+ */
+ function getTableAttributes($table, $field = '') {
+ $this->clean($table);
+ $this->clean($field);
+
+ if ($field == '') {
+ // This query is made much more complex by the addition of the 'attisserial' field.
+ // The subquery to get that field checks to see if there is an internally dependent
+ // sequence on the field.
+ $sql = "
+ SELECT
+ a.attname,
+ pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
+ a.atttypmod,
+ a.attnotnull, a.atthasdef, adef.adsrc,
+ a.attstattarget, a.attstorage, t.typstorage,
+ (
+ SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
+ WHERE pd.objid=pc.oid
+ AND pd.classid=pc.tableoid
+ AND pd.refclassid=pc.tableoid
+ AND pd.refobjid=a.attrelid
+ AND pd.refobjsubid=a.attnum
+ AND pd.deptype='i'
+ AND pc.relkind='S'
+ ) IS NOT NULL AS attisserial,
+ pg_catalog.col_description(a.attrelid, a.attnum) AS comment
+
+ FROM
+ pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
+ ON a.attrelid=adef.adrelid
+ AND a.attnum=adef.adnum
+ LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
+ WHERE
+ a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
+ nspname = '{$this->_schema}'))
+ AND a.attnum > 0 AND NOT a.attisdropped
+ ORDER BY a.attnum";
+ }
+ else {
+ $sql = "
+ SELECT
+ a.attname,
+ pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
+ pg_catalog.format_type(a.atttypid, NULL) as base_type,
+ a.atttypmod,
+ a.attnotnull, a.atthasdef, adef.adsrc,
+ a.attstattarget, a.attstorage, t.typstorage,
+ pg_catalog.col_description(a.attrelid, a.attnum) AS comment
+ FROM
+ pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
+ ON a.attrelid=adef.adrelid
+ AND a.attnum=adef.adnum
+ LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
+ WHERE
+ a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
+ nspname = '{$this->_schema}'))
+ AND a.attname = '{$field}'";
+ }
+
+ return $this->selectSet($sql);
}
/**
- * Empties a table in the database
- * @param $table The table to be emptied
- * @return 0 success
+ * Finds the names and schemas of parent tables (in order)
+ * @param $table The table to find the parents for
+ * @return A recordset
*/
- function emptyTable($table) {
- $this->fieldClean($table);
+ function getTableParents($table) {
+ $this->clean($table);
- $sql = "DELETE FROM \"{$table}\"";
+ $sql = "
+ SELECT
+ pn.nspname, relname
+ FROM
+ pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
+ WHERE
+ pc.oid=pi.inhparent
+ AND pc.relnamespace=pn.oid
+ AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
+ ORDER BY
+ pi.inhseqno
+ ";
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
/**
- * Renames a table
- * @param $table The table to be renamed
- * @param $newName The new name for the table
- * @return 0 success
+ * Finds the names and schemas of child tables
+ * @param $table The table to find the children for
+ * @return A recordset
*/
- /*XXX FIXME !! NOT USED ANYMORE ??
- function renameTable($table, $newName) {
- $this->fieldClean($table);
- $this->fieldClean($newName);
+ function getTableChildren($table) {
+ $this->clean($table);
- $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\"";
+ $sql = "
+ SELECT
+ pn.nspname, relname
+ FROM
+ pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
+ WHERE
+ pc.oid=pi.inhrelid
+ AND pc.relnamespace=pn.oid
+ AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
+ ";
- return $this->execute($sql);
- }*/
+ return $this->selectSet($sql);
+ }
/**
* Returns the SQL definition for the table.
@@ -1351,9 +1586,349 @@ class Postgres extends ADODB_base {
}
/**
+ * Creates a new table in the database
+ * @param $name The name of the table
+ * @param $fields The number of fields
+ * @param $field An array of field names
+ * @param $type An array of field types
+ * @param $array An array of '' or '[]' for each type if it's an array or not
+ * @param $length An array of field lengths
+ * @param $notnull An array of not null
+ * @param $default An array of default values
+ * @param $withoutoids True if WITHOUT OIDS, false otherwise
+ * @param $colcomment An array of comments
+ * @param $comment Table comment
+ * @param $tablespace The tablespace name ('' means none/default)
+ * @param $uniquekey An Array indicating the fields that are unique (those indexes that are set)
+ * @param $primarykey An Array indicating the field used for the primarykey (those indexes that are set)
+ * @return 0 success
+ * @return -1 no fields supplied
+ */
+ function createTable($name, $fields, $field, $type, $array, $length, $notnull,
+ $default, $withoutoids, $colcomment, $tblcomment, $tablespace,
+ $uniquekey, $primarykey) {
+ $this->fieldClean($name);
+ $this->clean($tblcomment);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $schema = $this->schema();
+
+ $found = false;
+ $first = true;
+ $comment_sql = ''; //Accumulate comments for the columns
+ $sql = "CREATE TABLE {$schema}\"{$name}\" (";
+ for ($i = 0; $i < $fields; $i++) {
+ $this->fieldClean($field[$i]);
+ $this->clean($type[$i]);
+ $this->clean($length[$i]);
+ $this->clean($colcomment[$i]);
+
+ // Skip blank columns - for user convenience
+ if ($field[$i] == '' || $type[$i] == '') continue;
+ // If not the first column, add a comma
+ if (!$first) $sql .= ", ";
+ else $first = false;
+
+ switch ($type[$i]) {
+ // Have to account for weird placing of length for with/without
+ // time zone types
+ case 'timestamp with time zone':
+ case 'timestamp without time zone':
+ $qual = substr($type[$i], 9);
+ $sql .= "\"{$field[$i]}\" timestamp";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ $sql .= $qual;
+ break;
+ case 'time with time zone':
+ case 'time without time zone':
+ $qual = substr($type[$i], 4);
+ $sql .= "\"{$field[$i]}\" time";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ $sql .= $qual;
+ break;
+ default:
+ $sql .= "\"{$field[$i]}\" {$type[$i]}";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ }
+ // Add array qualifier if necessary
+ if ($array[$i] == '[]') $sql .= '[]';
+ // Add other qualifiers
+ if (!isset($primarykey[$i])) {
+ if (isset($uniquekey[$i])) $sql .= " UNIQUE";
+ if (isset($notnull[$i])) $sql .= " NOT NULL";
+ }
+ if ($default[$i] != '') $sql .= " DEFAULT {$default[$i]}";
+
+ if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
+
+ $found = true;
+ }
+
+ if (!$found) return -1;
+
+ // PRIMARY KEY
+ $primarykeycolumns = array();
+ for ($i = 0; $i < $fields; $i++) {
+ if (isset($primarykey[$i])) {
+ $primarykeycolumns[] = "\"{$field[$i]}\"";
+ }
+ }
+ if (count($primarykeycolumns) > 0) {
+ $sql .= ", PRIMARY KEY (" . implode(", ", $primarykeycolumns) . ")";
+ }
+
+ $sql .= ")";
+
+ // WITHOUT OIDS
+ if ($this->hasWithoutOIDs() && $withoutoids)
+ $sql .= ' WITHOUT OIDS';
+
+ // Tablespace
+ if ($this->hasTablespaces() && $tablespace != '') {
+ $this->fieldClean($tablespace);
+ $sql .= " TABLESPACE \"{$tablespace}\"";
+ }
+
+ $status = $this->execute($sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ if ($tblcomment != '') {
+ $status = $this->setComment('TABLE', '', $name, $tblcomment, true);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ if ($comment_sql != '') {
+ $status = $this->execute($comment_sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+ return $this->endTransaction();
+ }
+
+ /**
+ * Creates a new table in the database copying attribs and other properties from another table
+ * @param $name The name of the table
+ * @param $like an array giving the schema ans the name of the table from which attribs are copying from:
+ * array(
+ * 'table' => table name,
+ * 'schema' => the schema name,
+ * )
+ * @param $defaults if true, copy the defaults values as well
+ * @param $constraints if true, copy the constraints as well (CHECK on table & attr)
+ * @param $tablespace The tablespace name ('' means none/default)
+ */
+ function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '') {
+ $this->fieldClean($name);
+
+ $this->fieldClean($like['schema']);
+ $this->fieldClean($like['table']);
+ $like = "\"{$like['schema']}\".\"{$like['table']}\"";
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $sql = "CREATE TABLE \"{$this->_schema}\".\"{$name}\" (LIKE {$like}";
+
+ if ($defaults) $sql .= " INCLUDING DEFAULTS";
+ if ($this->hasCreateTableLikeWithConstraints() && $constraints) $sql .= " INCLUDING CONSTRAINTS";
+ if ($this->hasCreateTableLikeWithIndexes() && $idx) $sql .= " INCLUDING INDEXES";
+
+ $sql .= ")";
+
+ if ($this->hasTablespaces() && $tablespace != '') {
+ $this->fieldClean($tablespace);
+ $sql .= " TABLESPACE \"{$tablespace}\"";
+ }
+
+ $status = $this->execute($sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Alter a table's name
+ * @param $tblrs The table RecordSet returned by getTable()
+ * @param $name The new table's name
+ * @return 0 success
+ */
+ function alterTableName($tblrs, $name = null) {
+ // Rename (only if name has changed)
+ if (!empty($name) && ($name != $tblrs->fields['relname'])) {
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$tblrs->fields['relname']}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status == 0)
+ $tblrs->fields['relname'] = $name;
+ else
+ return $status;
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a table's owner
+ * @param $tblrs The table RecordSet returned by getTable()
+ * @param $name The new table's owner
+ * @return 0 success
+ */
+ function alterTableOwner($tblrs, $owner = null) {
+ if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
+ // If owner has been changed, then do the alteration. We are
+ // careful to avoid this generally as changing owner is a
+ // superuser only function.
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";
+
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a table's tablespace
+ * @param $tblrs The table RecordSet returned by getTable()
+ * @param $name The new table's tablespace
+ * @return 0 success
+ */
+ function alterTableTablespace($tblrs, $tablespace = null) {
+ if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
+ // If tablespace has been changed, then do the alteration. We
+ // don't want to do this unnecessarily.
+ $sql = "ALTER TABLE \"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";
+
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a table's schema
+ * @param $tblrs The table RecordSet returned by getTable()
+ * @param $name The new table's schema
+ * @return 0 success
+ */
+ function alterTableSchema($tblrs, $schema = null) {
+ if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
+ // If tablespace has been changed, then do the alteration. We
+ // don't want to do this unnecessarily.
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$tblrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
+
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Protected method which alter a table
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $tblrs The table recordSet returned by getTable()
+ * @param $name The new name for the table
+ * @param $owner The new owner for the table
+ * @param $schema The new schema for the table
+ * @param $comment The comment on the table
+ * @param $tablespace The new tablespace for the table ('' means leave as is)
+ * @return 0 success
+ * @return -3 rename error
+ * @return -4 comment error
+ * @return -5 owner error
+ * @return -6 tablespace error
+ * @return -7 schema error
+ */
+ protected
+ function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
+
+ $this->fieldArrayClean($tblrs->fields);
+
+ // Comment
+ $this->clean($comment);
+ $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
+ if ($status != 0) return -4;
+
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterTableOwner($tblrs, $owner);
+ if ($status != 0) return -5;
+
+ // Tablespace
+ $this->fieldClean($tablespace);
+ $status = $this->alterTableTablespace($tblrs, $tablespace);
+ if ($status != 0) return -6;
+
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterTableName($tblrs, $name);
+ if ($status != 0) return -3;
+
+ // Schema
+ $this->fieldClean($schema);
+ $status = $this->alterTableSchema($tblrs, $schema);
+ if ($status != 0) return -7;
+
+ return 0;
+ }
+
+ /**
+ * Alter table properties
+ * @param $table The name of the table
+ * @param $name The new name for the table
+ * @param $owner The new owner for the table
+ * @param $schema The new schema for the table
+ * @param $comment The comment on the table
+ * @param $tablespace The new tablespace for the table ('' means leave as is)
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 get existing table error
+ * @return $this->_alterTable error code
+ */
+ function alterTable($table, $name, $owner, $schema, $comment, $tablespace) {
+
+ $this->fieldClean($table);
+ $data = $this->getTable($table);
+
+ if ($data->recordCount() != 1)
+ return -2;
+
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ $status = $this->_alterTable($data, $name, $owner, $schema, $comment, $tablespace);
+
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return $status;
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Returns the SQL for changing the current user
+ * @param $user The user to change to
+ * @return The SQL
+ */
+ function getChangeUserSQL($user) {
+ $this->clean($user);
+ return "SET SESSION AUTHORIZATION '{$user}';";
+ }
+
+ /**
* Given an array of attnums and a relation, returns an array mapping
- * atttribute number to attribute name. Relation could be a table OR
- * a view.
+ * attribute number to attribute name.
* @param $table The table to get attributes for
* @param $atts An array of attribute numbers
* @return An array mapping attnum to attname
@@ -1368,13 +1943,15 @@ class Postgres extends ADODB_base {
if (sizeof($atts) == 0) return array();
- $sql = "SELECT attnum, attname FROM pg_attribute WHERE attrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND attnum IN ('" .
- join("','", $atts) . "')";
+ $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
+ attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
+ relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
+ AND attnum IN ('" . join("','", $atts) . "')";
$rs = $this->selectSet($sql);
if ($rs->recordCount() != sizeof($atts)) {
- return -2;
- }
+ return -2;
+ }
else {
$temp = array();
while (!$rs->EOF) {
@@ -1386,6 +1963,34 @@ class Postgres extends ADODB_base {
}
/**
+ * Empties a table in the database
+ * @param $table The table to be emptied
+ * @return 0 success
+ */
+ function emptyTable($table) {
+ $this->fieldClean($table);
+
+ $sql = "DELETE FROM \"{$this->_schema}\".\"{$table}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes a table from the database
+ * @param $table The table to drop
+ * @param $cascade True to cascade drop, false to restrict
+ * @return 0 success
+ */
+ function dropTable($table, $cascade) {
+ $this->fieldClean($table);
+
+ $sql = "DROP TABLE \"{$this->_schema}\".\"{$table}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ /**
* Add a new column to a table
* @param $table The table to add to
* @param $column The name of the new column
@@ -1404,7 +2009,7 @@ class Postgres extends ADODB_base {
$this->clean($comment);
$schema = $this->schema();
-
+
if ($length == '')
$sql = "ALTER TABLE {$schema}\"{$table}\" ADD COLUMN \"{$column}\" {$type}";
else {
@@ -1443,99 +2048,130 @@ class Postgres extends ADODB_base {
$status = $this->execute($sql);
if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
+ $this->rollbackTransaction();
+ return -1;
+ }
$status = $this->setComment('COLUMN', $column, $table, $comment);
if ($status != 0) {
$this->rollbackTransaction();
return -1;
- }
+ }
return $this->endTransaction();
}
/**
- * Sets default value of a column
- * @param $table The table from which to drop
- * @param $column The column name to set
- * @param $default The new default value
+ * Alters a column in a table
+ * @param $table The table in which the column resides
+ * @param $column The column to alter
+ * @param $name The new name for the column
+ * @param $notnull (boolean) True if not null, false otherwise
+ * @param $oldnotnull (boolean) True if column is already not null, false otherwise
+ * @param $default The new default for the column
+ * @param $olddefault The old default for the column
+ * @param $type The new type for the column
+ * @param $array True if array type, false otherwise
+ * @param $length The optional size of the column (ie. 30 for varchar(30))
+ * @param $oldtype The old type for the column
+ * @param $comment Comment for the column
* @return 0 success
+ * @return -1 batch alteration failed
+ * @return -3 rename column error
+ * @return -4 comment error
+ * @return -6 transaction error
*/
- function setColumnDefault($table, $column, $default) {
+ function alterColumn($table, $column, $name, $notnull, $oldnotnull, $default, $olddefault,
+ $type, $length, $array, $oldtype, $comment) {
$this->fieldClean($table);
$this->fieldClean($column);
+ $this->clean($comment);
- $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
+ // Initialise an empty SQL string
+ $sql = '';
- return $this->execute($sql);
+ // Create the command for changing nullability
+ if ($notnull != $oldnotnull) {
+ $sql .= "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . (($notnull) ? 'SET' : 'DROP') . " NOT NULL";
}
- /**
- * Drops default value of a column
- * @param $table The table from which to drop
- * @param $column The column name to drop default
- * @return 0 success
- */
- function dropColumnDefault($table, $column) {
- $this->fieldClean($table);
- $this->fieldClean($column);
+ // Add default, if it has changed
+ if ($default != $olddefault) {
+ if ($default == '') {
+ if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
+ else $sql .= ", ";
+ $sql .= "ALTER COLUMN \"{$column}\" DROP DEFAULT";
+ }
+ else {
+ if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
+ else $sql .= ", ";
+ $sql .= "ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
+ }
+ }
- $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
+ // Add type, if it has changed
+ if ($length == '')
+ $ftype = $type;
+ else {
+ switch ($type) {
+ // Have to account for weird placing of length for with/without
+ // time zone types
+ case 'timestamp with time zone':
+ case 'timestamp without time zone':
+ $qual = substr($type, 9);
+ $ftype = "timestamp({$length}){$qual}";
+ break;
+ case 'time with time zone':
+ case 'time without time zone':
+ $qual = substr($type, 4);
+ $ftype = "time({$length}){$qual}";
+ break;
+ default:
+ $ftype = "{$type}({$length})";
+ }
+ }
- return $this->execute($sql);
- }
+ // Add array qualifier, if requested
+ if ($array) $ftype .= '[]';
- /**
- * Sets whether or not a column can contain NULLs
- * @param $table The table that contains the column
- * @param $column The column to alter
- * @param $state True to set null, false to set not null
- * @return 0 success
- * @return -1 attempt to set not null, but column contains nulls
- * @return -2 transaction error
- * @return -3 lock error
- * @return -4 update error
- */
- function setColumnNull($table, $column, $state) {
- $this->fieldClean($table);
- $this->fieldClean($column);
+ if ($ftype != $oldtype) {
+ if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
+ else $sql .= ", ";
+ $sql .= "ALTER COLUMN \"{$column}\" TYPE {$ftype}";
+ }
// Begin transaction
$status = $this->beginTransaction();
- if ($status != 0) return -2;
-
- // Properly lock the table
- $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";
- $status = $this->execute($sql);
if ($status != 0) {
$this->rollbackTransaction();
- return -3;
+ return -6;
}
- // Check for existing nulls
- if (!$state) {
- $sql = "SELECT COUNT(*) AS total FROM \"{$table}\" WHERE \"{$column}\" IS NULL";
- $result = $this->selectField($sql, 'total');
- if ($result > 0) {
+ // Attempt to process the batch alteration, if anything has been changed
+ if ($sql != '') {
+ $status = $this->execute($sql);
+ if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
}
- // Otherwise update the table. Note the reverse-sensed $state variable
- $sql = "UPDATE pg_attribute SET attnotnull = " . (($state) ? 'false' : 'true') . "
- WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '{$table}')
- AND attname = '{$column}'";
-
- $status = $this->execute($sql);
+ // Update the comment on the column
+ $status = $this->setComment('COLUMN', $column, $table, $comment);
if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
+ $this->rollbackTransaction();
+ return -4;
+ }
+
+ // Rename the column, if it has been changed
+ if ($column != $name) {
+ $status = $this->renameColumn($table, $column, $name);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
}
- // Otherwise, close the transaction
return $this->endTransaction();
}
@@ -1551,7 +2187,39 @@ class Postgres extends ADODB_base {
$this->fieldClean($column);
$this->fieldClean($newName);
- $sql = "ALTER TABLE \"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Sets default value of a column
+ * @param $table The table from which to drop
+ * @param $column The column name to set
+ * @param $default The new default value
+ * @return 0 success
+ */
+ function setColumnDefault($table, $column, $default) {
+ $this->fieldClean($table);
+ $this->fieldClean($column);
+
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Sets whether or not a column can contain NULLs
+ * @param $table The table that contains the column
+ * @param $column The column to alter
+ * @param $state True to set null, false to set not null
+ * @return 0 success
+ */
+ function setColumnNull($table, $column, $state) {
+ $this->fieldClean($table);
+ $this->fieldClean($column);
+
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . (($state) ? 'DROP' : 'SET') . " NOT NULL";
return $this->execute($sql);
}
@@ -1562,112 +2230,173 @@ class Postgres extends ADODB_base {
* @param $column The column to be dropped
* @param $cascade True to cascade drop, false to restrict
* @return 0 success
- * @return -99 not implemented
*/
function dropColumn($table, $column, $cascade) {
- return -99;
+ $this->fieldClean($table);
+ $this->fieldClean($column);
+
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" DROP COLUMN \"{$column}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
}
/**
- * Alters a column in a table OR view
- * @param $table The table in which the column resides
- * @param $column The column to alter
- * @param $name The new name for the column
- * @param $notnull (boolean) True if not null, false otherwise
- * @param $oldnotnull (boolean) True if column is already not null, false otherwise
- * @param $default The new default for the column
- * @param $olddefault The old default for the column
- * @param $type The new type for the column
- * @param $array True if array type, false otherwise
- * @param $length The optional size of the column (ie. 30 for varchar(30))
- * @param $oldtype The old type for the column
- * @param $comment Comment for the column
+ * Drops default value of a column
+ * @param $table The table from which to drop
+ * @param $column The column name to drop default
* @return 0 success
- * @return -1 set not null error
- * @return -2 set default error
- * @return -3 rename column error
- * @return -4 comment error
*/
- function alterColumn($table, $column, $name, $notnull, $oldnotnull, $default, $olddefault,
- $type, $length, $array, $oldtype, $comment) {
- $this->beginTransaction();
-
- // @@ NEED TO HANDLE "NESTED" TRANSACTION HERE
- if ($notnull != $oldnotnull) {
- $status = $this->setColumnNull($table, $column, !$notnull);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
+ function dropColumnDefault($table, $column) {
+ $this->fieldClean($table);
+ $this->fieldClean($column);
- // Set default, if it has changed
- if ($default != $olddefault) {
- if ($default == '')
- $status = $this->dropColumnDefault($table, $column);
- else
- $status = $this->setColumnDefault($table, $column, $default);
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
- }
+ return $this->execute($sql);
+ }
- // Rename the column, if it has been changed
- if ($column != $name) {
- $status = $this->renameColumn($table, $column, $name);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
+ /**
+ * Sets up the data object for a dump. eg. Starts the appropriate
+ * transaction, sets variables, etc.
+ * @return 0 success
+ */
+ function beginDump() {
+ $status = parent::beginDump();
+ if ($status != 0) return $status;
- // Parameters must be cleaned for the setComment function. It's ok to do
- // that here since this is the last time these variables are used.
- $this->fieldClean($name);
- $this->fieldClean($table);
- $this->clean($comment);
- $status = $this->setComment('COLUMN', $name, $table, $comment);
+ // Set extra_float_digits to 2
+ $sql = "SET extra_float_digits TO 2";
+ $status = $this->execute($sql);
if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
+ $this->rollbackTransaction();
+ return -1;
+ }
}
+ /**
+ * Ends the data object for a dump.
+ * @return 0 success
+ */
+ function endDump() {
return $this->endTransaction();
}
+ /**
+ * Returns a recordset of all columns in a relation. Used for data export.
+ * @@ Note: Really needs to use a cursor
+ * @param $relation The name of a relation
+ * @return A recordset on success
+ * @return -1 Failed to set datestyle
+ */
+ function dumpRelation($relation, $oids) {
+ $this->fieldClean($relation);
+
+ // Actually retrieve the rows
+ if ($oids) $oid_str = $this->id . ', ';
+ else $oid_str = '';
+
+ return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
+ }
+
// Row functions
/**
- * Delete a row from a table
- * @param $table The table from which to delete
- * @param $key An array mapping column => value to delete
- * @return 0 success
+ * Get the fields for uniquely identifying a row in a table
+ * @param $table The table for which to retrieve the identifier
+ * @return An array mapping attribute number to attribute name, empty for no identifiers
+ * @return -1 error
*/
- function deleteRow($table, $key) {
- if (!is_array($key)) return -1;
+ function getRowIdentifier($table) {
+ $oldtable = $table;
+ $this->clean($table);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ // Get the first primary or unique index (sorting primary keys first) that
+ // is NOT a partial index.
+ $sql = "
+ SELECT indrelid, indkey
+ FROM pg_catalog.pg_index
+ WHERE indisunique AND indrelid=(
+ SELECT oid FROM pg_catalog.pg_class
+ WHERE relname='{$table}' AND relnamespace=(
+ SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}'
+ )
+ ) AND indpred IS NULL AND indexprs IS NULL
+ ORDER BY indisprimary DESC LIMIT 1";
+ $rs = $this->selectSet($sql);
+
+ // If none, check for an OID column. Even though OIDs can be duplicated, the edit and delete row
+ // functions check that they're only modiying a single row. Otherwise, return empty array.
+ if ($rs->recordCount() == 0) {
+ // Check for OID column
+ $temp = array();
+ if ($this->hasObjectID($table)) {
+ $temp = array('oid');
+ }
+ $this->endTransaction();
+ return $temp;
+ }
+ // Otherwise find the names of the keys
else {
- // Begin transaction. We do this so that we can ensure only one row is
- // deleted
- $status = $this->beginTransaction();
- if ($status != 0) {
+ $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
+ if (!is_array($attnames)) {
$this->rollbackTransaction();
return -1;
}
-
- $status = $this->delete($table, $key, $this->_schema);
- if ($status != 0 || $this->conn->Affected_Rows() != 1) {
- $this->rollbackTransaction();
- return -2;
+ else {
+ $this->endTransaction();
+ return $attnames;
}
-
- // End transaction
- return $this->endTransaction();
}
}
/**
+ * Adds a new row to a table
+ * @param $table The table in which to insert
+ * @param $var An array mapping new values for the row
+ * @param $nulls An array mapping column => something if it is to be null
+ * @param $format An array of the data type (VALUE or EXPRESSION)
+ * @param $types An array of field types
+ * @return 0 success
+ * @return -1 invalid parameters
+ */
+ function insertRow($table, $vars, $nulls, $format, $types) {
+
+ if (!is_array($vars) || !is_array($nulls) || !is_array($format)
+ || !is_array($types)) return -1;
+ else {
+ $this->fieldClean($table);
+
+ $schema = $this->schema();
+
+ // Build clause
+ if (sizeof($vars) > 0) {
+ $fields = '';
+ $values = '';
+ foreach($vars as $key => $value) {
+ $this->fieldClean($key);
+
+ // Handle NULL values
+ if (isset($nulls[$key])) $tmp = 'NULL';
+ else $tmp = $this->formatValue($types[$key], $format[$key], $value);
+
+ if ($fields) $fields .= ", \"{$key}\"";
+ else $fields = "INSERT INTO {$schema}\"{$table}\" (\"{$key}\"";
+
+ if ($values) $values .= ", {$tmp}";
+ else $values = ") VALUES ({$tmp}";
+ }
+ $sql = $fields . $values . ')';
+ }
+ return $this->execute($sql);
+ }
+ }
+
+ /**
* Updates a row in a table
* @param $table The table in which to update
* @param $vars An array mapping new values for the row
@@ -1687,7 +2416,7 @@ class Postgres extends ADODB_base {
// Build clause
if (sizeof($vars) > 0) {
$schema = $this->schema();
-
+
foreach($vars as $key => $value) {
$this->fieldClean($key);
@@ -1708,247 +2437,155 @@ class Postgres extends ADODB_base {
}
else $sql .= " AND \"{$k}\"='{$v}'";
}
- }
+ }
// Begin transaction. We do this so that we can ensure only one row is
// edited
$status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
return -1;
- }
+ }
- $status = $this->execute($sql);
+ $status = $this->execute($sql);
if ($status != 0) { // update failed
- $this->rollbackTransaction();
+ $this->rollbackTransaction();
return -1;
} elseif ($this->conn->Affected_Rows() != 1) { // more than one row could be updated
$this->rollbackTransaction();
return -2;
- }
-
- // End transaction
- return $this->endTransaction();
}
- }
-
- /**
- * Adds a new row to a table
- * @param $table The table in which to insert
- * @param $var An array mapping new values for the row
- * @param $nulls An array mapping column => something if it is to be null
- * @param $format An array of the data type (VALUE or EXPRESSION)
- * @param $types An array of field types
- * @return 0 success
- * @return -1 invalid parameters
- */
- function insertRow($table, $vars, $nulls, $format, $types) {
-
- if (!is_array($vars) || !is_array($nulls) || !is_array($format)
- || !is_array($types)) return -1;
- else {
- $this->fieldClean($table);
- $schema = $this->schema();
-
- // Build clause
- if (sizeof($vars) > 0) {
- $fields = '';
- $values = '';
- foreach($vars as $key => $value) {
- $this->fieldClean($key);
-
- // Handle NULL values
- if (isset($nulls[$key])) $tmp = 'NULL';
- else $tmp = $this->formatValue($types[$key], $format[$key], $value);
-
- if ($fields) $fields .= ", \"{$key}\"";
- else $fields = "INSERT INTO {$schema}\"{$table}\" (\"{$key}\"";
-
- if ($values) $values .= ", {$tmp}";
- else $values = ") VALUES ({$tmp}";
- }
- $sql = $fields . $values . ')';
- }
- return $this->execute($sql);
- }
+ // End transaction
+ return $this->endTransaction();
}
-
- /**
- * Returns a recordset of all columns in a table
- * @param $table The name of a table
- * @param $key The associative array holding the key to retrieve
- * @return A recordset
- */
- function browseRow($table, $key) {
- $this->fieldClean($table);
-
- $schema = $this->schema();
-
- $sql = "SELECT * FROM {$schema}\"{$table}\"";
- if (is_array($key) && sizeof($key) > 0) {
- $sql .= " WHERE true";
- foreach ($key as $k => $v) {
- $this->fieldClean($k);
- $this->clean($v);
- $sql .= " AND \"{$k}\"='{$v}'";
- }
- }
-
- return $this->selectSet($sql);
}
/**
- * Get the fields for uniquely identifying a row in a table
- * @param $table The table for which to retrieve the identifier
- * @return An array mapping attribute number to attribute name, empty for no identifiers
- * @return -1 error
+ * Delete a row from a table
+ * @param $table The table from which to delete
+ * @param $key An array mapping column => value to delete
+ * @return 0 success
*/
- function getRowIdentifier($table) {
- $oldtable = $table;
- $this->clean($table);
-
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- // Get the first primary or unique index (sorting primary keys first) that
- // is NOT a partial index.
- $sql = "SELECT indrelid, indkey FROM pg_index WHERE indisunique AND indrelid=(SELECT oid FROM pg_class
- WHERE relname='{$table}') AND indpred='' AND indproc=0 ORDER BY indisprimary DESC LIMIT 1";
- $rs = $this->selectSet($sql);
-
- // If none, check for an OID column. Even though OIDs can be duplicated, the edit and delete row
- // functions check that they're only modiying a single row. Otherwise, return empty array.
- if ($rs->recordCount() == 0) {
- // Check for OID column
- $temp = array();
- if ($this->hasObjectID($table)) {
- $temp = array('oid');
- }
- $this->endTransaction();
- return $temp;
- }
- // Otherwise find the names of the keys
+ function deleteRow($table, $key) {
+ if (!is_array($key)) return -1;
else {
- $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
- if (!is_array($attnames)) {
+ // Begin transaction. We do this so that we can ensure only one row is
+ // deleted
+ $status = $this->beginTransaction();
+ if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
- else {
- $this->endTransaction();
- return $attnames;
+
+ $status = $this->delete($table, $key, $this->_schema);
+ if ($status != 0 || $this->conn->Affected_Rows() != 1) {
+ $this->rollbackTransaction();
+ return -2;
}
+
+ // End transaction
+ return $this->endTransaction();
}
}
// Sequence functions
/**
- * Returns all sequences in the current database
- * @return A recordset
- */
- function getSequences($all = false) {
- // $all argument is ignored as it makes no difference
- $sql = "SELECT
- c.relname AS seqname,
- u.usename AS seqowner,
- (SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS seqcomment
- FROM
- pg_class c, pg_user u
- WHERE c.relowner=u.usesysid AND c.relkind = 'S' ORDER BY seqname";
-
- return $this->selectSet( $sql );
- }
-
- /**
* Returns properties of a single sequence
* @param $sequence Sequence name
* @return A recordset
*/
function getSequence($sequence) {
- $temp = $sequence;
- // Need both field cleaned and literal cleaned versions
$this->fieldClean($sequence);
- $this->clean($temp);
- $sql = "SELECT '{$sequence}' AS seqname, s.*,
- (SELECT description FROM pg_description pd WHERE pd.objoid=(SELECT oid FROM pg_class WHERE relname='{$temp}')) AS seqcomment,
- u.usename AS seqowner
- FROM \"{$sequence}\" AS s, pg_user u, pg_class c
- WHERE
- c.relowner = u.usesysid AND c.relkind = 'S'
- AND c.relname = '{$sequence}'";
+ $sql = "
+ SELECT c.relname AS seqname, s.*,
+ pg_catalog.obj_description(s.tableoid, 'pg_class') AS seqcomment,
+ u.usename AS seqowner, n.nspname
+ FROM \"{$sequence}\" AS s, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
+ WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
+ AND c.relname = '{$sequence}' AND c.relkind = 'S' AND n.nspname='{$this->_schema}'
+ AND n.oid = c.relnamespace";
return $this->selectSet( $sql );
}
/**
- * Drops a given sequence
- * @param $sequence Sequence name
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
+ * Returns all sequences in the current database
+ * @return A recordset
*/
- function dropSequence($sequence, $cascade) {
- $this->fieldClean($sequence);
-
- $sql = "DROP SEQUENCE \"{$sequence}\"";
- if ($cascade) $sql .= " CASCADE";
+ function getSequences($all = false) {
+ if ($all) {
+ // Exclude pg_catalog and information_schema tables
+ $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
+ FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
+ WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
+ AND c.relkind = 'S'
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
+ ORDER BY nspname, seqname";
+ } else {
+ $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment,
+ (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
+ FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
+ WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
+ AND c.relkind = 'S' AND n.nspname='{$this->_schema}' ORDER BY seqname";
+ }
- return $this->execute($sql);
+ return $this->selectSet( $sql );
}
/**
- * Resets a given sequence to min value of sequence
+ * Execute nextval on a given sequence
* @param $sequence Sequence name
* @return 0 success
* @return -1 sequence not found
*/
- function resetSequence($sequence) {
- // Get the minimum value of the sequence
- $seq = $this->getSequence($sequence);
- if ($seq->recordCount() != 1) return -1;
- $minvalue = $seq->fields[$this->sqFields['minvalue']];
-
+ function nextvalSequence($sequence) {
/* This double-cleaning is deliberate */
$this->fieldClean($sequence);
$this->clean($sequence);
- $sql = "SELECT SETVAL('\"{$sequence}\"', {$minvalue})";
+ $sql = "SELECT pg_catalog.NEXTVAL('\"{$this->_schema}\".\"{$sequence}\"')";
return $this->execute($sql);
}
/**
- * Execute nextval on a given sequence
+ * Execute setval on a given sequence
* @param $sequence Sequence name
+ * @param $nextvalue The next value
* @return 0 success
* @return -1 sequence not found
*/
- function nextvalSequence($sequence) {
+ function setvalSequence($sequence, $nextvalue) {
/* This double-cleaning is deliberate */
$this->fieldClean($sequence);
$this->clean($sequence);
+ $this->clean($nextvalue);
- $sql = "SELECT NEXTVAL('\"{$sequence}\"')";
+ $sql = "SELECT pg_catalog.SETVAL('\"{$this->_schema}\".\"{$sequence}\"', '{$nextvalue}')";
return $this->execute($sql);
}
/**
- * Execute setval on a given sequence
+ * Resets a given sequence to min value of sequence
* @param $sequence Sequence name
- * @param $nextvalue The next value
* @return 0 success
* @return -1 sequence not found
*/
- function setvalSequence($sequence, $nextvalue) {
+ function resetSequence($sequence) {
+ // Get the minimum value of the sequence
+ $seq = $this->getSequence($sequence);
+ if ($seq->recordCount() != 1) return -1;
+ $minvalue = $seq->fields['min_value'];
+
/* This double-cleaning is deliberate */
$this->fieldClean($sequence);
$this->clean($sequence);
- $this->clean($nextvalue);
- $sql = "SELECT SETVAL('\"{$sequence}\"', '{$nextvalue}')";
+ $sql = "SELECT pg_catalog.SETVAL('\"{$this->_schema}\".\"{$sequence}\"', {$minvalue})";
return $this->execute($sql);
}
@@ -1974,7 +2611,7 @@ class Postgres extends ADODB_base {
$this->clean($cachevalue);
$schema = $this->schema();
-
+
$sql = "CREATE SEQUENCE {$schema}\"{$sequence}\"";
if ($increment != '') $sql .= " INCREMENT {$increment}";
if ($minvalue != '') $sql .= " MINVALUE {$minvalue}";
@@ -1988,17 +2625,81 @@ class Postgres extends ADODB_base {
/**
* Rename a sequence
- * @param $sequence The sequence name
+ * @param $seqrs The sequence RecordSet returned by getSequence()
* @param $name The new name for the sequence
* @return 0 success
*/
- function renameSequence($sequence, $name) {
- $this->fieldClean($name);
- $this->fieldClean($sequence);
+ function alterSequenceName($seqrs, $name) {
+ if (!empty($name) && ($seqrs->fields['seqname'] != $name)) {
+ $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$seqrs->fields['seqname']}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status == 0)
+ $seqrs->fields['seqname'] = $name;
+ else
+ return $status;
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a sequence's owner
+ * @param $seqrs The sequence RecordSet returned by getSequence()
+ * @param $name The new owner for the sequence
+ * @return 0 success
+ */
+ function alterSequenceOwner($seqrs, $owner) {
+ // If owner has been changed, then do the alteration. We are
+ // careful to avoid this generally as changing owner is a
+ // superuser only function.
+ if (!empty($owner) && ($seqrs->fields['seqowner'] != $owner)) {
+ $sql = "ALTER TABLE \"{$seqrs->fields['seqname']}\" OWNER TO \"{$owner}\"";
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a sequence's schema
+ * @param $seqrs The sequence RecordSet returned by getSequence()
+ * @param $name The new schema for the sequence
+ * @return 0 success
+ */
+ function alterSequenceSchema($seqrs, $schema) {
+ if (!empty($schema) && ($seqrs->fields['nspname'] != $schema)) {
+ $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$seqrs->fields['seqname']}\" SET SCHEMA {$schema}";
+ return $this->execute($sql);
+ }
+ return 0;
+ }
- $sql = "ALTER TABLE \"{$sequence}\" RENAME TO \"{$name}\"";
+ /**
+ * Alter a sequence's properties
+ * @param $seqrs The sequence RecordSet returned by getSequence()
+ * @param $increment The sequence incremental value
+ * @param $minvalue The sequence minimum value
+ * @param $maxvalue The sequence maximum value
+ * @param $startvalue The sequence current value
+ * @param $cachevalue Thesequence cache value
+ * @param $cycledvalue Sequence can cycle ?
+ * @return 0 success
+ */
+ function alterSequenceProps($seqrs, $increment, $minvalue, $maxvalue,
+ $startvalue, $cachevalue, $cycledvalue) {
+
+ $sql = '';
+ if (!empty($increment) && ($increment != $seqrs->fields['increment_by'])) $sql .= " INCREMENT {$increment}";
+ if (!empty($minvalue) && ($minvalue != $seqrs->fields['min_value'])) $sql .= " MINVALUE {$minvalue}";
+ if (!empty($maxvalue) && ($maxvalue != $seqrs->fields['max_value'])) $sql .= " MAXVALUE {$maxvalue}";
+ if (!empty($startvalue) && ($startvalue != $seqrs->fields['last_value'])) $sql .= " RESTART {$startvalue}";
+ if (!empty($cachevalue) && ($cachevalue != $seqrs->fields['cache_value'])) $sql .= " CACHE {$cachevalue}";
+ // toggle cycle yes/no
+ if (!is_null($cycledvalue)) $sql .= (!$cycledvalue ? ' NO ' : '') . " CYCLE";
+ if ($sql != '') {
+ $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$seqrs->fields['seqname']}\" {$sql}";
return $this->execute($sql);
}
+ return 0;
+ }
/**
* Protected method which alter a sequence
@@ -2018,29 +2719,50 @@ class Postgres extends ADODB_base {
* @return -3 rename error
* @return -4 comment error
* @return -5 owner error
+ * @return -6 get sequence props error
* @return -7 schema error
*/
- /*protected*/
+ protected
function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
+ $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
- $sequence = $seqrs->fields['seqname'];
- $this->fieldClean($name);
- $this->clean($comment);
- /* $owner, $schema, $increment, $minvalue, $maxvalue,
- * $startvalue, $cachevalue, $cycledvalue not supported in pg70 */
+ $this->fieldArrayClean($seqrs->fields);
// Comment
- $status = $this->setComment('SEQUENCE', $sequence, '', $comment);
+ $this->clean($comment);
+ $status = $this->setComment('SEQUENCE', $seqrs->fields['seqname'], '', $comment);
if ($status != 0)
return -4;
- // Rename (only if name has changed)
- if ($name != $sequence) {
- $status = $this->renameSequence($sequence, $name);
- if ($status != 0)
- return -3;
- }
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterSequenceOwner($seqrs, $owner);
+ if ($status != 0)
+ return -5;
+
+ // Props
+ $this->clean($increment);
+ $this->clean($minvalue);
+ $this->clean($maxvalue);
+ $this->clean($startvalue);
+ $this->clean($cachevalue);
+ $this->clean($cycledvalue);
+ $status = $this->alterSequenceProps($seqrs, $increment, $minvalue,
+ $maxvalue, $startvalue, $cachevalue, $cycledvalue);
+ if ($status != 0)
+ return -6;
+
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterSequenceName($seqrs, $name);
+ if ($status != 0)
+ return -3;
+
+ // Schema
+ $this->clean($schema);
+ $status = $this->alterSequenceSchema($seqrs, $schema);
+ if ($status != 0)
+ return -7;
return 0;
}
@@ -2064,10 +2786,12 @@ class Postgres extends ADODB_base {
* @return $this->_alterSequence error code
*/
function alterSequence($sequence, $name, $comment, $owner=null, $schema=null, $increment=null,
- $minvalue=null, $maxvalue=null, $startvalue=null, $cachevalue=null, $cycledvalue=null) {
+ $minvalue=null, $maxvalue=null, $startvalue=null, $cachevalue=null, $cycledvalue=null) {
$this->fieldClean($sequence);
+
$data = $this->getSequence($sequence);
+
if ($data->recordCount() != 1)
return -2;
@@ -2088,239 +2812,249 @@ class Postgres extends ADODB_base {
return $this->endTransaction();
}
- // Constraint functions
-
/**
- * Returns a list of all constraints on a table
- * @param $table The table to find rules for
- * @return A recordset
+ * Drops a given sequence
+ * @param $sequence Sequence name
+ * @param $cascade True to cascade drop, false to restrict
+ * @return 0 success
*/
- function getConstraints($table) {
- $this->clean($table);
+ function dropSequence($sequence, $cascade) {
+ $this->fieldClean($sequence);
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
+ $sql = "DROP SEQUENCE \"{$this->_schema}\".\"{$sequence}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ // View functions
+
+ /**
+ * Returns all details for a particular view
+ * @param $view The name of the view to retrieve
+ * @return View info
+ */
+ function getView($view) {
+ $this->clean($view);
$sql = "
- SELECT
- rcname AS conname,
- 'CHECK (' || rcsrc || ')' AS consrc,
- 'c' AS contype,
- NULL::int2vector AS indkey
- FROM
- pg_relcheck
- WHERE
- rcrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
- UNION ALL
- SELECT
- pc.relname,
- NULL,
- CASE WHEN indisprimary THEN
- 'p'
- ELSE
- 'u'
- END,
- indkey
- FROM
- pg_class pc,
- pg_index pi
- WHERE
- pc.oid=pi.indexrelid
- AND (pi.indisunique OR pi.indisprimary)
- AND pi.indrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
- ORDER BY
- 1
- ";
+ SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.pg_get_viewdef(c.oid, true) AS vwdefinition,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
+ WHERE (c.relname = '$view') AND n.nspname='{$this->_schema}'";
return $this->selectSet($sql);
}
/**
- * Adds a check constraint to a table
- * @param $table The table to which to add the check
- * @param $definition The definition of the check
- * @param $name (optional) The name to give the check, otherwise default name is assigned
- * @return 0 success
+ * Returns a list of all views in the database
+ * @return All views
*/
- function addCheckConstraint($table, $definition, $name = '') {
- $this->fieldClean($table);
- $this->fieldClean($name);
- // @@ How the heck do you clean a definition???
-
- $sql = "ALTER TABLE \"{$table}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "CHECK ({$definition})";
+ function getViews() {
+ $sql = "
+ SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
+ WHERE (n.nspname='{$this->_schema}') AND (c.relkind = 'v'::\"char\")
+ ORDER BY relname";
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
/**
- * Drops a check constraint from a table
- * @param $table The table from which to drop the check
- * @param $name The name of the check to be dropped
+ * Updates a view.
+ * @param $viewname The name fo the view to update
+ * @param $definition The new definition for the view
* @return 0 success
- * @return -2 transaction error
- * @return -3 lock error
- * @return -4 check drop error
+ * @return -1 transaction error
+ * @return -2 drop view error
+ * @return -3 create view error
*/
- function dropCheckConstraint($table, $name) {
- $this->clean($table);
- $this->clean($name);
+ function setView($viewname, $definition,$comment) {
+ return $this->createView($viewname, $definition, true, $comment);
+ }
- // Begin transaction
+ /**
+ * Creates a new view.
+ * @param $viewname The name of the view to create
+ * @param $definition The definition for the new view
+ * @param $replace True to replace the view, false otherwise
+ * @return 0 success
+ */
+ function createView($viewname, $definition, $replace, $comment) {
$status = $this->beginTransaction();
- if ($status != 0) return -2;
+ if ($status != 0) return -1;
- // Properly lock the table
- $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
+ $this->fieldClean($viewname);
+ $this->clean($comment);
- // Delete the check constraint
- $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND rcname='{$name}'";
- $status = $this->execute($sql);
- if ($status != 0) {
+ // Note: $definition not cleaned
+
+ $sql = "CREATE ";
+ if ($replace) $sql .= "OR REPLACE ";
+ $sql .= "VIEW ". $this->schema() ."\"{$viewname}\" AS {$definition}";
+
+ $status = $this->execute($sql);
+ if ($status) {
$this->rollbackTransaction();
- return -4;
+ return -1;
}
- // Update the pg_class catalog to reflect the new number of checks
- $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
- rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}'))
- WHERE relname='{$table}'";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
+ if ($comment != '') {
+ $status = $this->setComment('VIEW', $viewname, '', $comment);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
}
- // Otherwise, close the transaction
return $this->endTransaction();
}
- // Constraint functions
-
/**
- * Removes a constraint from a relation
- * @param $constraint The constraint to drop
- * @param $relation The relation from which to drop
- * @param $type The type of constraint (c, f, u or p)
- * @param $cascade True to cascade drop, false to restrict
+ * Rename a view
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new view's name
* @return 0 success
- * @return -99 dropping foreign keys not supported
*/
- function dropConstraint($constraint, $relation, $type, $cascade) {
- $this->fieldClean($constraint);
- $this->fieldClean($relation);
-
- switch ($type) {
- case 'c':
- // CHECK constraint
- return $this->dropCheckConstraint($relation, $constraint);
- break;
- case 'p':
- case 'u':
- // PRIMARY KEY or UNIQUE constraint
- return $this->dropIndex($constraint, $cascade);
- break;
- case 'f':
- // FOREIGN KEY constraint
- return -99;
+ function alterViewName($vwrs, $name) {
+ // Rename (only if name has changed)
+ if (!empty($name) && ($name != $vwrs->fields['relname'])) {
+ $sql = "ALTER VIEW \"{$this->_schema}\".\"{$vwrs->fields['relname']}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status == 0)
+ $vwrs->fields['relname'] = $name;
+ else
+ return $status;
}
+ return 0;
}
/**
- * Adds a unique constraint to a table
- * @param $table The table to which to add the unique
- * @param $fields (array) An array of fields over which to add the unique
- * @param $name (optional) The name to give the unique, otherwise default name is assigned
+ * Alter a view's owner
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new view's owner
* @return 0 success
- * @return -1 invalid fields
*/
- function addUniqueKey($table, $fields, $name = '') {
- if (!is_array($fields) || sizeof($fields) == 0) return -1;
- $this->fieldClean($table);
- $this->fieldArrayClean($fields);
+ function alterViewOwner($vwrs, $owner = null) {
+ if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) {
+ // If owner has been changed, then do the alteration. We are
+ // careful to avoid this generally as changing owner is a
+ // superuser only function.
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$vwrs->fields['relname']}\" OWNER TO \"{$owner}\"";
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Alter a view's schema
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new view's schema
+ * @return 0 success
+ */
+ function alterViewSchema($vwrs, $schema) {
+ if (!empty($schema) && ($vwrs->fields['nspname'] != $schema)) {
+ // If tablespace has been changed, then do the alteration. We
+ // don't want to do this unnecessarily.
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$vwrs->fields['relname']}\" SET SCHEMA \"{$schema}\"";
+ return $this->execute($sql);
+ }
+ return 0;
+ }
+
+ /**
+ * Protected method which alter a view
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new name for the view
+ * @param $owner The new owner for the view
+ * @param $comment The comment on the view
+ * @return 0 success
+ * @return -3 rename error
+ * @return -4 comment error
+ * @return -5 owner error
+ * @return -6 schema error
+ */
+ protected
+ function _alterView($vwrs, $name, $owner, $schema, $comment) {
+
+ $this->fieldArrayClean($vwrs->fields);
+
+ // Comment
+ $this->clean($comment);
+ if ($this->setComment('VIEW', $vwrs->fields['relname'], '', $comment) != 0)
+ return -4;
+
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterViewOwner($vwrs, $owner);
+ if ($status != 0) return -5;
+
+ // Rename
$this->fieldClean($name);
+ $status = $this->alterViewName($vwrs, $name);
+ if ($status != 0) return -3;
- if ($name != '')
- $sql = "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" . join('","', $fields) . "\")";
- else return -99; // Not supported
+ // Schema
+ $this->fieldClean($schema);
+ $status = $this->alterViewSchema($vwrs, $schema);
+ if ($status != 0) return -6;
- return $this->execute($sql);
+ return 0;
}
/**
- * Adds a foreign key constraint to a table
- * @param $targschema The schema that houses the target table to which to add the foreign key
- * @param $targtable The table to which to add the foreign key
- * @param $target The table that contains the target columns
- * @param $sfields (array) An array of source fields over which to add the foreign key
- * @param $tfields (array) An array of target fields over which to add the foreign key
- * @param $upd_action The action for updates (eg. RESTRICT)
- * @param $del_action The action for deletes (eg. RESTRICT)
- * @param $match The match type (eg. MATCH FULL)
- * @param $deferrable The deferrability (eg. NOT DEFERRABLE)
- * @param $intially The initial deferrability (eg. INITIALLY IMMEDIATE)
- * @param $name (optional) The name to give the key, otherwise default name is assigned
+ * Alter view properties
+ * @param $view The name of the view
+ * @param $name The new name for the view
+ * @param $owner The new owner for the view
+ * @param $schema The new schema for the view
+ * @param $comment The comment on the view
* @return 0 success
- * @return -1 no fields given
+ * @return -1 transaction error
+ * @return -2 get existing view error
+ * @return $this->_alterView error code
*/
- function addForeignKey($table, $targschema, $targtable, $sfields, $tfields, $upd_action, $del_action,
- $match, $deferrable, $initially, $name = '') {
- if (!is_array($sfields) || sizeof($sfields) == 0 ||
- !is_array($tfields) || sizeof($tfields) == 0) return -1;
- $this->fieldClean($table);
- $this->fieldClean($targschema);
- $this->fieldClean($targtable);
- $this->fieldArrayClean($sfields);
- $this->fieldArrayClean($tfields);
- $this->fieldClean($name);
+ function alterView($view, $name, $owner, $schema, $comment) {
- $schema = $this->schema();
-
- $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "FOREIGN KEY (\"" . join('","', $sfields) . "\") ";
- $sql .= "REFERENCES ";
- // Target table needs to be fully qualified
- if ($this->hasSchemas()) {
- $sql .= "\"{$targschema}\".";
+ $this->fieldClean($view);
+ $data = $this->getView($view);
+ if ($data->recordCount() != 1)
+ return -2;
+
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
}
- $sql .= "\"{$targtable}\"(\"" . join('","', $tfields) . "\") ";
- if ($match != $this->fkmatches[0]) $sql .= " {$match}";
- if ($upd_action != $this->fkactions[0]) $sql .= " ON UPDATE {$upd_action}";
- if ($del_action != $this->fkactions[0]) $sql .= " ON DELETE {$del_action}";
- if ($deferrable != $this->fkdeferrable[0]) $sql .= " {$deferrable}";
- if ($initially != $this->fkinitial[0]) $sql .= " {$initially}";
- return $this->execute($sql);
+ $status = $this->_alterView($data, $name, $owner, $schema, $comment);
+
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return $status;
+ }
+
+ return $this->endTransaction();
}
/**
- * Adds a primary key constraint to a table
- * @param $table The table to which to add the primery key
- * @param $fields (array) An array of fields over which to add the primary key
- * @param $name (optional) The name to give the key, otherwise default name is assigned
+ * Drops a view.
+ * @param $viewname The name of the view to drop
+ * @param $cascade True to cascade drop, false to restrict
* @return 0 success
*/
- function addPrimaryKey($table, $fields, $name = '') {
- // This function can be faked with a unique index and a catalog twiddle, however
- // how do we ensure that it's only used on NOT NULL fields?
- return -99; // Not supported.
- }
+ function dropView($viewname, $cascade) {
+ $this->fieldClean($viewname);
- /**
- * Finds the foreign keys that refer to the specified table
- * @param $table The table to find referrers for
- * @return A recordset
- */
- function getReferrers($table) {
- // In PostgreSQL < 7.3, there is no way to discover foreign keys
- return -99;
+ $sql = "DROP VIEW ". $this->schema() ."\"{$viewname}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
}
// Index functions
@@ -2333,10 +3067,13 @@ class Postgres extends ADODB_base {
*/
function getIndexes($table = '', $unique = false) {
$this->clean($table);
- $sql = "SELECT c2.relname AS indname, i.indisprimary, i.indisunique, pg_get_indexdef(i.indexrelid) AS inddef,
- obj_description(c.oid, 'pg_index') AS idxcomment
- FROM pg_class c, pg_class c2, pg_index i
- WHERE c.relname = '{$table}' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
+
+ $sql = "
+ SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
+ pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
+ FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
+ WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
+ AND c.oid = i.indrelid AND i.indexrelid = c2.oid
";
if ($unique) $sql .= " AND i.indisunique ";
$sql .= " ORDER BY c2.relname";
@@ -2382,7 +3119,7 @@ class Postgres extends ADODB_base {
// Predicate
if ($this->hasPartialIndexes() && trim($where) != '') {
$sql .= " WHERE ({$where})";
- }
+ }
return $this->execute($sql);
}
@@ -2396,654 +3133,974 @@ class Postgres extends ADODB_base {
function dropIndex($index, $cascade) {
$this->fieldClean($index);
- $sql = "DROP INDEX \"{$index}\"";
+ $sql = "DROP INDEX \"{$this->_schema}\".\"{$index}\"";
if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
}
/**
+ * Rebuild indexes
+ * @param $type 'DATABASE' or 'TABLE' or 'INDEX'
+ * @param $name The name of the specific database, table, or index to be reindexed
+ * @param $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in 7.2-7.3, ignored in >=7.4
+ */
+ function reindex($type, $name, $force = false) {
+ $this->fieldClean($name);
+ switch($type) {
+ case 'DATABASE':
+ $sql = "REINDEX {$type} \"{$name}\"";
+ if ($force) $sql .= ' FORCE';
+ break;
+ case 'TABLE':
+ case 'INDEX':
+ $sql = "REINDEX {$type} \"{$this->_schema}\".\"{$name}\"";
+ if ($force) $sql .= ' FORCE';
+ break;
+ default:
+ return -1;
+ }
+
+ return $this->execute($sql);
+ }
+
+ /**
* Clusters an index
* @param $index The name of the index
* @param $table The table the index is on
* @return 0 success
*/
function clusterIndex($index, $table) {
+
$this->fieldClean($index);
$this->fieldClean($table);
// We don't bother with a transaction here, as there's no point rolling
// back an expensive cluster if a cheap analyze fails for whatever reason
- $sql = "CLUSTER \"{$index}\" ON \"{$table}\"";
+ $sql = "CLUSTER \"{$this->_schema}\".\"{$table}\" USING \"{$index}\"";
return $this->execute($sql);
}
- // Rule functions
+ // Constraint functions
/**
- * Returns a list of all rules on a table
+ * Returns a list of all constraints on a table
* @param $table The table to find rules for
* @return A recordset
*/
- function getRules($table) {
+ function getConstraints($table) {
$this->clean($table);
+ // This SQL is greatly complicated by the need to retrieve
+ // index clustering information for primary and unique constraints
$sql = "SELECT
- *
+ pc.conname,
+ pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
+ pc.contype,
+ CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
+ SELECT
+ indisclustered
+ FROM
+ pg_catalog.pg_depend pd,
+ pg_catalog.pg_class pl,
+ pg_catalog.pg_index pi
+ WHERE
+ pd.refclassid=pc.tableoid
+ AND pd.refobjid=pc.oid
+ AND pd.objid=pl.oid
+ AND pl.oid=pi.indexrelid
+ ) ELSE
+ NULL
+ END AS indisclustered
FROM
- pg_rules
+ pg_catalog.pg_constraint pc
WHERE
- tablename='{$table}'
+ pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}'))
ORDER BY
- rulename
+ 1
";
return $this->selectSet($sql);
}
/**
- * Removes a rule from a relation
- * @param $rule The rule to drop
- * @param $relation The relation from which to drop (unused)
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
+ * Returns a list of all constraints on a table,
+ * including constraint name, definition, related col and referenced namespace,
+ * table and col if needed
+ * @param $table the table where we are looking for fk
+ * @return a recordset
*/
- function dropRule($rule, $relation, $cascade) {
- $this->fieldClean($rule);
+ function getConstraintsWithFields($table) {
+ global $data;
- $sql = "DROP RULE \"{$rule}\"";
- if ($cascade) $sql .= " CASCADE";
+ $data->clean($table);
- return $this->execute($sql);
+ // get the max number of col used in a constraint for the table
+ $sql = "SELECT DISTINCT
+ max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb
+ FROM
+ pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
+ JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
+ WHERE
+ r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
+
+ $rs = $this->selectSet($sql);
+
+ if ($rs->EOF) $max_col = 0;
+ else $max_col = $rs->fields['nb'];
+
+ $sql = '
+ SELECT
+ c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
+ ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
+ r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
+ pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
+ FROM
+ pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
+ JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
+ for ($i = 2; $i <= $rs->fields['nb']; $i++) {
+ $sql.= " OR f1.attnum=c.conkey[$i]";
+ }
+ $sql.= '))
+ JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
+ LEFT JOIN (
+ pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
+ ) ON (c.confrelid=r2.oid)
+ LEFT JOIN pg_catalog.pg_attribute AS f2 ON
+ (f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
+ for ($i = 2; $i <= $rs->fields['nb']; $i++)
+ $sql.= "OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
+
+ $sql .= sprintf("))
+ WHERE
+ r1.relname = '%s' AND ns1.nspname='%s'
+ ORDER BY 1", $table, $this->_schema);
+
+ return $this->selectSet($sql);
}
/**
- * Creates a rule
- * @param $name The name of the new rule
- * @param $event SELECT, INSERT, UPDATE or DELETE
- * @param $table Table on which to create the rule
- * @param $where When to execute the rule, '' indicates always
- * @param $instead True if an INSTEAD rule, false otherwise
- * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
- * @param $action The action to take
- * @param $replace (optional) True to replace existing rule, false otherwise
+ * Adds a primary key constraint to a table
+ * @param $table The table to which to add the primery key
+ * @param $fields (array) An array of fields over which to add the primary key
+ * @param $name (optional) The name to give the key, otherwise default name is assigned
+ * @param $tablespace (optional) The tablespace for the schema, '' indicates default.
* @return 0 success
- * @return -1 invalid event
+ * @return -1 no fields given
*/
- function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false) {
- $this->fieldClean($name);
+ function addPrimaryKey($table, $fields, $name = '', $tablespace = '') {
+ if (!is_array($fields) || sizeof($fields) == 0) return -1;
$this->fieldClean($table);
- if (!in_array($event, $this->rule_events)) return -1;
+ $this->fieldArrayClean($fields);
+ $this->fieldClean($name);
+ $this->fieldClean($tablespace);
$schema = $this->schema();
- $sql = "CREATE";
- if ($replace) $sql .= " OR REPLACE";
- $sql .= " RULE \"{$name}\" AS ON {$event} TO {$schema}\"{$table}\"";
- // Can't escape WHERE clause
- if ($where != '') $sql .= " WHERE {$where}";
- $sql .= " DO";
- if ($instead) $sql .= " INSTEAD";
- if ($type == 'NOTHING')
- $sql .= " NOTHING";
- else $sql .= " ({$action})";
+ $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
+ if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
+ $sql .= "PRIMARY KEY (\"" . join('","', $fields) . "\")";
+
+ if ($tablespace != '' && $this->hasTablespaces())
+ $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
return $this->execute($sql);
}
/**
- * Edits a rule
- * @param $name The name of the new rule
- * @param $event SELECT, INSERT, UPDATE or DELETE
- * @param $table Table on which to create the rule
- * @param $where When to execute the rule, '' indicates always
- * @param $instead True if an INSTEAD rule, false otherwise
- * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
- * @param $action The action to take
+ * Adds a unique constraint to a table
+ * @param $table The table to which to add the unique key
+ * @param $fields (array) An array of fields over which to add the unique key
+ * @param $name (optional) The name to give the key, otherwise default name is assigned
+ * @param $tablespace (optional) The tablespace for the schema, '' indicates default.
* @return 0 success
- * @return -1 invalid event
- * @return -2 transaction error
- * @return -3 drop existing rule error
- * @return -4 create new rule error
+ * @return -1 no fields given
*/
- function setRule($name, $event, $table, $where, $instead, $type, $action) {
- $status = $this->beginTransaction();
- if ($status != 0) return -2;
-
- $status = $this->dropRule($name, $table);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
-
- $status = $this->createRule($name, $event, $table, $where, $instead, $type, $action);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- $status = $this->endTransaction();
- return ($status == 0) ? 0 : -2;
- }
-
- // View functions
+ function addUniqueKey($table, $fields, $name = '', $tablespace = '') {
+ if (!is_array($fields) || sizeof($fields) == 0) return -1;
+ $this->fieldClean($table);
+ $this->fieldArrayClean($fields);
+ $this->fieldClean($name);
+ $this->fieldClean($tablespace);
- /**
- * Returns a list of all views in the database
- * @return All views
- */
- function getViews() {
- global $conf;
+ $schema = $this->schema();
- if (!$conf['show_system'])
- $where = " WHERE viewname NOT LIKE 'pg@_%' ESCAPE '@' ";
- else
- $where = '';
+ $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
+ if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
+ $sql .= "UNIQUE (\"" . join('","', $fields) . "\")";
- $sql = "SELECT viewname AS relname, viewowner AS relowner, definition AS vwdefinition,
- (SELECT description FROM pg_description pd, pg_class pc
- WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
- FROM pg_views v
- {$where}
- ORDER BY relname";
+ if ($tablespace != '' && $this->hasTablespaces())
+ $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
- return $this->selectSet($sql);
+ return $this->execute($sql);
}
/**
- * Returns all details for a particular view
- * @param $view The name of the view to retrieve
- * @return View info
+ * Adds a check constraint to a table
+ * @param $table The table to which to add the check
+ * @param $definition The definition of the check
+ * @param $name (optional) The name to give the check, otherwise default name is assigned
+ * @return 0 success
*/
- function getView($view) {
- $this->clean($view);
+ function addCheckConstraint($table, $definition, $name = '') {
+ $this->fieldClean($table);
+ $this->fieldClean($name);
+ // @@ How the heck do you clean a definition???
- $sql = "SELECT viewname AS relname, NULL AS nspname, viewowner AS relowner, definition AS vwdefinition,
- (SELECT description FROM pg_description pd, pg_class pc
- WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
- FROM pg_views v
- WHERE viewname='{$view}'";
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ADD ";
+ if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
+ $sql .= "CHECK ({$definition})";
- return $this->selectSet($sql);
+ return $this->execute($sql);
}
/**
- * Creates a new view.
- * @param $viewname The name of the view to create
- * @param $definition The definition for the new view
- * @param $replace True to replace the view, false otherwise
+ * Drops a check constraint from a table
+ * @param $table The table from which to drop the check
+ * @param $name The name of the check to be dropped
* @return 0 success
+ * @return -2 transaction error
+ * @return -3 lock error
+ * @return -4 check drop error
*/
- function createView($viewname, $definition, $replace, $comment) {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $this->fieldClean($viewname);
- $this->clean($comment);
-
- // Note: $definition not cleaned
+ function dropCheckConstraint($table, $name) {
+ $this->clean($table);
+ $this->clean($name);
- $sql = "CREATE ";
- if ($replace) $sql .= "OR REPLACE ";
- $sql .= "VIEW ". $this->schema() ."\"{$viewname}\" AS {$definition}";
+ // Begin transaction
+ $status = $this->beginTransaction();
+ if ($status != 0) return -2;
+ // Properly lock the table
+ $sql = "LOCK TABLE \"{$this->_schema}\".\"{$table}\" IN ACCESS EXCLUSIVE MODE";
$status = $this->execute($sql);
- if ($status) {
+ if ($status != 0) {
$this->rollbackTransaction();
- return -1;
+ return -3;
+ }
+
+ // Delete the check constraint
+ $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
+ nspname = '{$this->_schema}')) AND rcname='{$name}'";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
}
- if ($comment != '') {
- $status = $this->setComment('VIEW', $viewname, '', $comment);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
+ // Update the pg_class catalog to reflect the new number of checks
+ $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
+ rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
+ nspname = '{$this->_schema}')))
+ WHERE relname='{$table}'";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
}
+ // Otherwise, close the transaction
return $this->endTransaction();
}
/**
- * Drops a view.
- * @param $viewname The name of the view to drop
- * @param $cascade True to cascade drop, false to restrict
+ * Adds a foreign key constraint to a table
+ * @param $targschema The schema that houses the target table to which to add the foreign key
+ * @param $targtable The table to which to add the foreign key
+ * @param $target The table that contains the target columns
+ * @param $sfields (array) An array of source fields over which to add the foreign key
+ * @param $tfields (array) An array of target fields over which to add the foreign key
+ * @param $upd_action The action for updates (eg. RESTRICT)
+ * @param $del_action The action for deletes (eg. RESTRICT)
+ * @param $match The match type (eg. MATCH FULL)
+ * @param $deferrable The deferrability (eg. NOT DEFERRABLE)
+ * @param $intially The initial deferrability (eg. INITIALLY IMMEDIATE)
+ * @param $name (optional) The name to give the key, otherwise default name is assigned
* @return 0 success
+ * @return -1 no fields given
*/
- function dropView($viewname, $cascade) {
- $this->fieldClean($viewname);
+ function addForeignKey($table, $targschema, $targtable, $sfields, $tfields, $upd_action, $del_action,
+ $match, $deferrable, $initially, $name = '') {
+ if (!is_array($sfields) || sizeof($sfields) == 0 ||
+ !is_array($tfields) || sizeof($tfields) == 0) return -1;
+ $this->fieldClean($table);
+ $this->fieldClean($targschema);
+ $this->fieldClean($targtable);
+ $this->fieldArrayClean($sfields);
+ $this->fieldArrayClean($tfields);
+ $this->fieldClean($name);
- $sql = "DROP VIEW ". $this->schema() ."\"{$viewname}\"";
- if ($cascade) $sql .= " CASCADE";
+ $schema = $this->schema();
+
+ $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
+ if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
+ $sql .= "FOREIGN KEY (\"" . join('","', $sfields) . "\") ";
+ $sql .= "REFERENCES ";
+ // Target table needs to be fully qualified
+ if ($this->hasSchemas()) {
+ $sql .= "\"{$targschema}\".";
+ }
+ $sql .= "\"{$targtable}\"(\"" . join('","', $tfields) . "\") ";
+ if ($match != $this->fkmatches[0]) $sql .= " {$match}";
+ if ($upd_action != $this->fkactions[0]) $sql .= " ON UPDATE {$upd_action}";
+ if ($del_action != $this->fkactions[0]) $sql .= " ON DELETE {$del_action}";
+ if ($deferrable != $this->fkdeferrable[0]) $sql .= " {$deferrable}";
+ if ($initially != $this->fkinitial[0]) $sql .= " {$initially}";
return $this->execute($sql);
}
/**
- * Updates a view. Postgres 7.1 and below don't have CREATE OR REPLACE view,
- * so we do it with a drop and a recreate.
- * @param $viewname The name fo the view to update
- * @param $definition The new definition for the view
+ * Removes a constraint from a relation
+ * @param $constraint The constraint to drop
+ * @param $relation The relation from which to drop
+ * @param $type The type of constraint (c, f, u or p)
+ * @param $cascade True to cascade drop, false to restrict
* @return 0 success
- * @return -1 transaction error
- * @return -2 drop view error
- * @return -3 create view error
- * @return -4 comment error
*/
- function setView($viewname, $definition, $comment) {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $status = $this->dropView($viewname, false);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
+ function dropConstraint($constraint, $relation, $type, $cascade) {
+ $this->fieldClean($constraint);
+ $this->fieldClean($relation);
- $status = $this->createView($viewname, $definition, false, $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
+ if ($cascade) $sql .= " CASCADE";
- $status = $this->endTransaction();
- return ($status == 0) ? 0 : -1;
+ return $this->execute($sql);
}
+
/**
- * Rename a view
- * @param $view The current view's name
- * @param $name The new view's name
- * @return -1 Failed
- * @return 0 success
+ * A function for getting all columns linked by foreign keys given a group of tables
+ * @param $tables multi dimensional assoc array that holds schema and table name
+ * @return A recordset of linked tables and columns
+ * @return -1 $tables isn't an array
*/
- function renameView($view, $name) {
- $this->fieldClean($name);
- $this->fieldClean($view);
- $sql = "ALTER TABLE \"{$view}\" RENAME TO \"{$name}\"";
- if ($this->execute($sql) != 0)
- return -1;
- return 0;
- }
-
- /**
- * Protected method which alter a view
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $vwrs The view recordSet returned by getView()
- * @param $name The new name for the view
- * @param $owner The new owner for the view
- * @param $comment The comment on the view
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 schema error
- */
- function _alterView($vwrs, $name, $owner, $schema, $comment) {
+ function getLinkingKeys($tables) {
+ if (!is_array($tables)) return -1;
- $this->fieldClean($name);
- $this->clean($comment);
+ $tables_list = "'{$tables[0]['tablename']}'";
+ $schema_list = "'{$tables[0]['schemaname']}'";
+ $schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'";
+ for ($i = 1; $i < sizeof($tables); $i++) {
+ $tables_list .= ", '{$tables[$i]['tablename']}'";
+ $schema_list .= ", '{$tables[$i]['schemaname']}'";
+ $schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'";
+ }
+ $maxDimension = 1;
- $view = $vwrs->fields['relname'];
+ $sql = "
+ SELECT DISTINCT
+ array_dims(pc.conkey) AS arr_dim,
+ pgc1.relname AS p_table
+ FROM
+ pg_catalog.pg_constraint AS pc,
+ pg_catalog.pg_class AS pgc1
+ WHERE
+ pc.contype = 'f'
+ AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
+ AND pgc1.relname IN ($tables_list)
+ ";
- // Comment
- if ($this->setComment('VIEW', $view, '', $comment) != 0)
- return -4;
+ //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
+ $rs = $this->selectSet($sql);
+ while (!$rs->EOF) {
+ $arrData = explode(':', $rs->fields['arr_dim']);
+ $tmpDimension = intval(substr($arrData[1], 0, strlen($arrData[1] - 1)));
+ $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
+ $rs->MoveNext();
+ }
- // Rename (only if name has changed)
- if ($name != $view) {
- if ($this->renameView($view, $name) != 0)
- return -3;
+ //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
+ $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
+ for ($i = 2; $i <= $maxDimension; $i++) {
+ $cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) ";
}
+ $cons_str .= ') ';
- return 0;
+ $sql = "
+ SELECT
+ pgc1.relname AS p_table,
+ pgc2.relname AS f_table,
+ pfield.attname AS p_field,
+ cfield.attname AS f_field,
+ pgns1.nspname AS p_schema,
+ pgns2.nspname AS f_schema
+ FROM
+ pg_catalog.pg_constraint AS pc,
+ pg_catalog.pg_class AS pgc1,
+ pg_catalog.pg_class AS pgc2,
+ pg_catalog.pg_attribute AS pfield,
+ pg_catalog.pg_attribute AS cfield,
+ (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns1,
+ (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns2
+ WHERE
+ pc.contype = 'f'
+ AND pgc1.relnamespace = pgns1.ns_id
+ AND pgc2.relnamespace = pgns2.ns_id
+ AND pc.conrelid = pgc1.relfilenode
+ AND pc.confrelid = pgc2.relfilenode
+ AND pfield.attrelid = pc.conrelid
+ AND cfield.attrelid = pc.confrelid
+ AND $cons_str
+ AND pgns1.nspname || '.' || pgc1.relname IN ($schema_tables_list)
+ AND pgns2.nspname || '.' || pgc2.relname IN ($schema_tables_list)
+ ";
+ return $this->selectSet($sql);
}
/**
- * Alter table properties
- * @param $table The name of the view
- * @param $name The new name for the view
- * @param $owner The new owner for the view
- * @param $schema The new schema for the view
- * @param $comment The comment on the view
- * @return 0 success
- * @return -1 transaction error
- * @return -2 get existing view error
- * @return $this->_alterView error code
+ * Finds the foreign keys that refer to the specified table
+ * @param $table The table to find referrers for
+ * @return A recordset
*/
- function alterView($view, $name, $owner, $schema, $comment) {
-
- $this->fieldClean($view);
- $data = $this->getView($view);
- if ($data->recordCount() != 1)
- return -2;
+ function getReferrers($table) {
+ $this->clean($table);
$status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
+ if ($status != 0) return -1;
- $status = $this->_alterView($data, $name, $owner, $schema, $comment);
+ $sql = "
+ SELECT
+ pn.nspname,
+ pl.relname,
+ pc.conname,
+ pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
+ FROM
+ pg_catalog.pg_constraint pc,
+ pg_catalog.pg_namespace pn,
+ pg_catalog.pg_class pl
+ WHERE
+ pc.connamespace = pn.oid
+ AND pc.conrelid = pl.oid
+ AND pc.contype = 'f'
+ AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}'))
+ ORDER BY 1,2,3
+ ";
- if ($status != 0) {
- $this->rollbackTransaction();
- return $status;
+ return $this->selectSet($sql);
}
- return $this->endTransaction();
- }
-
- // Operator functions
+ // Domain functions
/**
- * Returns a list of all operators in the database
- * @return All operators
+ * Gets all information for a single domain
+ * @param $domain The name of the domain to fetch
+ * @return A recordset
*/
- function getOperators() {
- global $conf;
- if (!$conf['show_system'])
- $where = "WHERE po.oid > '{$this->_lastSystemOID}'::oid";
- else $where = '';
+ function getDomain($domain) {
+ $this->clean($domain);
$sql = "
SELECT
- po.oid,
- po.oprname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
- (SELECT description FROM pg_description pd WHERE po.oid=pd.objoid) AS oprcomment
+ t.typname AS domname,
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
+ t.typnotnull AS domnotnull,
+ t.typdefault AS domdef,
+ pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
+ pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
FROM
- pg_operator po
- {$where}
- ORDER BY
- po.oprname, oprleftname, oprrightname
- ";
+ pg_catalog.pg_type t
+ WHERE
+ t.typtype = 'd'
+ AND t.typname = '{$domain}'
+ AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname = '{$this->_schema}')";
return $this->selectSet($sql);
- }
+ }
/**
- * Returns all details for a particular operator
- * @param $operator_oid The oid of the operator
- * @return Function info
+ * Return all domains in current schema. Excludes domain constraints.
+ * @return All tables, sorted alphabetically
*/
- function getOperator($operator_oid) {
- $this->clean($operator_oid);
-
+ function getDomains() {
$sql = "
SELECT
- po.oid,
- po.oprname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
- po.oprcanhash,
- (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprcom) AS oprcom,
- (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprnegate) AS oprnegate,
- (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprlsortop) AS oprlsortop,
- (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprltcmpop) AS oprltcmpop,
- (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprgtcmpop) AS oprgtcmpop,
- po.oprcode::regproc AS oprcode,
- --(SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprcode) AS oprcode,
- (SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprrest) AS oprrest,
- (SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprjoin) AS oprjoin
+ t.typname AS domname,
+ pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
+ t.typnotnull AS domnotnull,
+ t.typdefault AS domdef,
+ pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
+ pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
FROM
- pg_operator po
+ pg_catalog.pg_type t
WHERE
- po.oid='{$operator_oid}'
- ";
+ t.typtype = 'd'
+ AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}')
+ ORDER BY t.typname";
return $this->selectSet($sql);
}
/**
- * Drops an operator
- * @param $operator_oid The OID of the operator to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
+ * Get domain constraints
+ * @param $domain The name of the domain whose constraints to fetch
+ * @return A recordset
*/
- function dropOperator($operator_oid, $cascade) {
- // Function comes in with $object as operator OID
- $opr = $this->getOperator($operator_oid);
- $this->fieldClean($opr->fields['oprname']);
-
- $schema = $this->schema();
+ function getDomainConstraints($domain) {
+ $this->clean($domain);
- $sql = "DROP OPERATOR {$schema}{$opr->fields['oprname']} (";
- // Quoting or formatting here???
- if ($opr->fields['oprleftname'] !== null) $sql .= $opr->fields['oprleftname'] . ', ';
- else $sql .= "NONE, ";
- if ($opr->fields['oprrightname'] !== null) $sql .= $opr->fields['oprrightname'] . ')';
- else $sql .= "NONE)";
-
- if ($cascade) $sql .= " CASCADE";
+ $sql = "
+ SELECT
+ conname,
+ contype,
+ pg_catalog.pg_get_constraintdef(oid, true) AS consrc
+ FROM
+ pg_catalog.pg_constraint
+ WHERE
+ contypid = (
+ SELECT oid FROM pg_catalog.pg_type
+ WHERE typname='{$domain}'
+ AND typnamespace = (
+ SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname = '{$this->_schema}')
+ )
+ ORDER BY conname";
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
- // User functions
-
/**
- * Changes a user's password
- * @param $username The username
- * @param $password The new password
+ * Creates a domain
+ * @param $domain The name of the domain to create
+ * @param $type The base type for the domain
+ * @param $length Optional type length
+ * @param $array True for array type, false otherwise
+ * @param $notnull True for NOT NULL, false otherwise
+ * @param $default Default value for domain
+ * @param $check A CHECK constraint if there is one
* @return 0 success
*/
- function changePassword($username, $password) {
- $this->fieldClean($username);
- $this->clean($password);
+ function createDomain($domain, $type, $length, $array, $notnull, $default, $check) {
+ $this->fieldClean($domain);
- $sql = "ALTER USER \"{$username}\" WITH PASSWORD '{$password}'";
+ $sql = "CREATE DOMAIN \"{$this->_schema}\".\"{$domain}\" AS ";
- return $this->execute($sql);
- }
+ if ($length == '')
+ $sql .= $type;
+ else {
+ switch ($type) {
+ // Have to account for weird placing of length for with/without
+ // time zone types
+ case 'timestamp with time zone':
+ case 'timestamp without time zone':
+ $qual = substr($type, 9);
+ $sql .= "timestamp({$length}){$qual}";
+ break;
+ case 'time with time zone':
+ case 'time without time zone':
+ $qual = substr($type, 4);
+ $sql .= "time({$length}){$qual}";
+ break;
+ default:
+ $sql .= "{$type}({$length})";
+ }
+ }
- /**
- * Returns all users in the database cluster
- * @return All users
- */
- function getUsers() {
- $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
- if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
- $sql .= " FROM pg_user ORDER BY usename";
+ // Add array qualifier, if requested
+ if ($array) $sql .= '[]';
- return $this->selectSet($sql);
+ if ($notnull) $sql .= ' NOT NULL';
+ if ($default != '') $sql .= " DEFAULT {$default}";
+ if ($this->hasDomainConstraints() && $check != '') $sql .= " CHECK ({$check})";
+
+ return $this->execute($sql);
}
/**
- * Returns information about a single user
- * @param $username The username of the user to retrieve
- * @return The user's data
+ * Alters a domain
+ * @param $domain The domain to alter
+ * @param $domdefault The domain default
+ * @param $domnotnull True for NOT NULL, false otherwise
+ * @param $domowner The domain owner
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 default error
+ * @return -3 not null error
+ * @return -4 owner error
*/
- function getUser($username) {
- $this->clean($username);
+ function alterDomain($domain, $domdefault, $domnotnull, $domowner) {
+ $this->fieldClean($domain);
+ $this->fieldClean($domowner);
- $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
- if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
- $sql .= " FROM pg_user WHERE usename='{$username}'";
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
- return $this->selectSet($sql);
- }
+ // Default
+ if ($domdefault == '')
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP DEFAULT";
+ else
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" SET DEFAULT {$domdefault}";
- /**
- * Determines whether or not a user is a super user
- * @param $username The username of the user
- * @return True if is a super user, false otherwise
- */
- function isSuperUser($username) {
- $this->clean($username);
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
- if (function_exists('pg_parameter_status')) {
- $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
- if ($val !== false) return $val == 'on';
+ // NOT NULL
+ if ($domnotnull)
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" SET NOT NULL";
+ else
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP NOT NULL";
+
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
}
- $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
+ // Owner
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" OWNER TO \"{$domowner}\"";
- $usesuper = $this->selectField($sql, 'usesuper');
- if ($usesuper == -1) return false;
- else return $usesuper == 't';
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
+ }
+
+ return $this->endTransaction();
}
/**
- * Creates a new user
- * @param $username The username of the user to create
- * @param $password A password for the user
- * @param $createdb boolean Whether or not the user can create databases
- * @param $createuser boolean Whether or not the user can create other users
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
- * @param $group (array) The groups to create the user in
+ * Drops a domain.
+ * @param $domain The name of the domain to drop
+ * @param $cascade True to cascade drop, false to restrict
* @return 0 success
*/
- function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {
- $this->fieldClean($username);
- $this->clean($password);
- $this->clean($expiry);
- $this->fieldArrayClean($groups);
+ function dropDomain($domain, $cascade) {
+ $this->fieldClean($domain);
- $sql = "CREATE USER \"{$username}\"";
- if ($password != '') $sql .= " WITH PASSWORD '{$password}'";
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
- if (is_array($groups) && sizeof($groups) > 0) $sql .= " IN GROUP \"" . join('", "', $groups) . "\"";
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
- else $sql .= " VALID UNTIL 'infinity'";
+ $sql = "DROP DOMAIN \"{$this->_schema}\".\"{$domain}\"";
+ if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
}
/**
- * Adjusts a user's info
- * @param $username The username of the user to modify
- * @param $password A new password for the user
- * @param $createdb boolean Whether or not the user can create databases
- * @param $createuser boolean Whether or not the user can create other users
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
+ * Adds a check constraint to a domain
+ * @param $domain The domain to which to add the check
+ * @param $definition The definition of the check
+ * @param $name (optional) The name to give the check, otherwise default name is assigned
* @return 0 success
*/
- function setUser($username, $password, $createdb, $createuser, $expiry) {
- $this->fieldClean($username);
- $this->clean($password);
- $this->clean($expiry);
+ function addDomainCheckConstraint($domain, $definition, $name = '') {
+ $this->fieldClean($domain);
+ $this->fieldClean($name);
- $sql = "ALTER USER \"{$username}\"";
- if ($password != '') $sql .= " WITH PASSWORD '{$password}'";
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
- else $sql .= " VALID UNTIL 'infinity'";
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" ADD ";
+ if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
+ $sql .= "CHECK ({$definition})";
return $this->execute($sql);
}
/**
- * Removes a user
- * @param $username The username of the user to drop
+ * Drops a domain constraint
+ * @param $domain The domain from which to remove the constraint
+ * @param $constraint The constraint to remove
+ * @param $cascade True to cascade, false otherwise
* @return 0 success
*/
- function dropUser($username) {
- $this->fieldClean($username);
+ function dropDomainConstraint($domain, $constraint, $cascade) {
+ $this->fieldClean($domain);
+ $this->fieldClean($constraint);
- $sql = "DROP USER \"{$username}\"";
+ $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP CONSTRAINT \"{$constraint}\"";
+ if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
}
- // Group functions
+ // Function functions
/**
- * Returns all groups in the database cluser
- * @return All groups
+ * Returns all details for a particular function
+ * @param $func The name of the function to retrieve
+ * @return Function info
*/
- function getGroups() {
- $sql = "SELECT groname FROM pg_group ORDER BY groname";
+ function getFunction($function_oid) {
+ $this->clean($function_oid);
+
+ $sql = "
+ SELECT
+ pc.oid AS prooid, proname, pg_catalog.pg_get_userbyid(proowner) AS proowner,
+ nspname as proschema, lanname as prolanguage, procost, prorows,
+ pg_catalog.format_type(prorettype, NULL) as proresult, prosrc,
+ probin, proretset, proisstrict, provolatile, prosecdef,
+ pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
+ proargnames AS proargnames,
+ pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
+ proconfig
+ FROM
+ pg_catalog.pg_proc pc, pg_catalog.pg_language pl,
+ pg_catalog.pg_namespace pn
+ WHERE
+ pc.oid = '{$function_oid}'::oid AND pc.prolang = pl.oid
+ AND pc.pronamespace = pn.oid
+ ";
return $this->selectSet($sql);
}
/**
- * Returns users in a specific group
- * @param $groname The name of the group
- * @return All users in the group
+ * Returns a list of all functions in the database
+ * @param $all If true, will find all available functions, if false just those in search path
+ * @param $type If not null, will find all functions with return value = type
+ *
+ * @return All functions
*/
- function getGroup($groname) {
- $this->clean($groname);
+ function getFunctions($all = false, $type = null) {
+ if ($all) {
+ $where = 'pg_catalog.pg_function_is_visible(p.oid)';
+ $distinct = 'DISTINCT ON (p.proname)';
- $sql = "SELECT grolist FROM pg_group WHERE groname = '{$groname}'";
-
- $grodata = $this->selectSet($sql);
- if ($grodata->fields['grolist'] !== null && $grodata->fields['grolist'] != '{}') {
- $members = $grodata->fields['grolist'];
- $members = ereg_replace("\{|\}","",$members);
- $this->clean($members);
-
- $sql = "SELECT usename FROM pg_user WHERE usesysid IN ({$members}) ORDER BY usename";
+ if ($type) {
+ $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
+ }
+ }
+ else {
+ $where = "n.nspname = '{$this->_schema}'";
+ $distinct = '';
}
- else $sql = "SELECT usename FROM pg_user WHERE false";
+
+ $sql = "
+ SELECT
+ {$distinct}
+ p.oid AS prooid,
+ p.proname,
+ p.proretset,
+ pg_catalog.format_type(p.prorettype, NULL) AS proresult,
+ pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
+ pl.lanname AS prolanguage,
+ pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment,
+ p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
+ CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
+ u.usename AS proowner
+ FROM pg_catalog.pg_proc p
+ INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
+ INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
+ LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
+ WHERE NOT p.proisagg
+ AND {$where}
+ ORDER BY p.proname, proresult
+ ";
return $this->selectSet($sql);
}
/**
- * Creates a new group
- * @param $groname The name of the group
- * @param $users An array of users to add to the group
- * @return 0 success
+ * Returns an array containing a function's properties
+ * @param $f The array of data for the function
+ * @return An array containing the properties
*/
- function createGroup($groname, $users) {
- $this->fieldClean($groname);
+ function getFunctionProperties($f) {
+ $temp = array();
- $sql = "CREATE GROUP \"{$groname}\"";
+ // Volatility
+ if ($f['provolatile'] == 'v')
+ $temp[] = 'VOLATILE';
+ elseif ($f['provolatile'] == 'i')
+ $temp[] = 'IMMUTABLE';
+ elseif ($f['provolatile'] == 's')
+ $temp[] = 'STABLE';
+ else
+ return -1;
- if (is_array($users) && sizeof($users) > 0) {
- $this->fieldArrayClean($users);
- $sql .= ' WITH USER "' . join('", "', $users) . '"';
- }
+ // Null handling
+ $f['proisstrict'] = $this->phpBool($f['proisstrict']);
+ if ($f['proisstrict'])
+ $temp[] = 'RETURNS NULL ON NULL INPUT';
+ else
+ $temp[] = 'CALLED ON NULL INPUT';
- return $this->execute($sql);
+ // Security
+ $f['prosecdef'] = $this->phpBool($f['prosecdef']);
+ if ($f['prosecdef'])
+ $temp[] = 'SECURITY DEFINER';
+ else
+ $temp[] = 'SECURITY INVOKER';
+
+ return $temp;
}
/**
- * Removes a group
- * @param $groname The name of the group to drop
+ * Updates (replaces) a function.
+ * @param $function_oid The OID of the function
+ * @param $funcname The name of the function to create
+ * @param $newname The new name for the function
+ * @param $args The array of argument types
+ * @param $returns The return type
+ * @param $definition The definition for the new function
+ * @param $language The language the function is written for
+ * @param $flags An array of optional flags
+ * @param $setof True if returns a set, false otherwise
+ * @param $comment The comment on the function
* @return 0 success
+ * @return -1 transaction error
+ * @return -3 create function error
+ * @return -4 comment error
+ * @return -5 rename function error
+ * @return -6 alter owner error
+ * @return -7 alter schema error
*/
- function dropGroup($groname) {
- $this->fieldClean($groname);
+ function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $funcown, $newown, $funcschema, $newschema, $cost, $rows, $comment) {
+ // Begin a transaction
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
- $sql = "DROP GROUP \"{$groname}\"";
+ // Replace the existing function
+ $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, true);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
- return $this->execute($sql);
+ // Comment on the function
+ $this->fieldClean($funcname);
+ $this->clean($comment);
+ $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
+ }
+
+ // Rename the function, if necessary
+ $this->fieldClean($newname);
+ if ($funcname != $newname) {
+ $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -5;
+ }
+
+ $funcname = $newname;
+ }
+
+ // Alter the owner, if necessary
+ if ($this->hasFunctionAlterOwner()) {
+ $this->fieldClean($newown);
+ if ($funcown != $newown) {
+ $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -6;
+ }
+ }
+
+ }
+
+ // Alter the schema, if necessary
+ if ($this->hasFunctionAlterSchema()) {
+ $this->fieldClean($newschema);
+ if ($funcschema != $newschema) {
+ $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -7;
+ }
+ }
+ }
+
+ return $this->endTransaction();
}
/**
- * Adds a group member
- * @param $groname The name of the group
- * @param $user The name of the user to add to the group
+ * Creates a new function.
+ * @param $funcname The name of the function to create
+ * @param $args A comma separated string of types
+ * @param $returns The return type
+ * @param $definition The definition for the new function
+ * @param $language The language the function is written for
+ * @param $flags An array of optional flags
+ * @param $setof True if it returns a set, false otherwise
+ * @param $rows number of rows planner should estimate will be returned
+ * @param $cost cost the planner should use in the function execution step
+ * @param $replace (optional) True if OR REPLACE, false for normal
* @return 0 success
*/
- function addGroupMember($groname, $user) {
- $this->fieldClean($groname);
- $this->fieldClean($user);
+ function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $replace = false) {
+ $this->fieldClean($funcname);
+ $this->clean($args);
+ $this->clean($language);
+ $this->arrayClean($flags);
+ $this->clean($cost);
+ $this->clean($rows);
- $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
+ $sql = "CREATE";
+ if ($replace) $sql .= " OR REPLACE";
+ $sql .= " FUNCTION \"{$this->_schema}\".\"{$funcname}\" (";
+
+ if ($args != '')
+ $sql .= $args;
+
+ // For some reason, the returns field cannot have quotes...
+ $sql .= ") RETURNS ";
+ if ($setof) $sql .= "SETOF ";
+ $sql .= "{$returns} AS ";
+
+ if (is_array($definition)) {
+ $this->arrayClean($definition);
+ $sql .= "'" . $definition[0] . "'";
+ if ($definition[1]) {
+ $sql .= ",'" . $definition[1] . "'";
+ }
+ } else {
+ $this->clean($definition);
+ $sql .= "'" . $definition . "'";
+ }
+
+ $sql .= " LANGUAGE \"{$language}\"";
+
+ // Add costs
+ if (!empty($cost))
+ $sql .= " COST {$cost}";
+
+ if ($rows <> 0 ){
+ $sql .= " ROWS {$rows}";
+ }
+
+ // Add flags
+ foreach ($flags as $v) {
+ // Skip default flags
+ if ($v == '') continue;
+ else $sql .= "\n{$v}";
+ }
return $this->execute($sql);
}
/**
- * Removes a group member
- * @param $groname The name of the group
- * @param $user The name of the user to remove from the group
+ * Drops a function.
+ * @param $function_oid The OID of the function to drop
+ * @param $cascade True to cascade drop, false to restrict
* @return 0 success
*/
- function dropGroupMember($groname, $user) {
- $this->fieldClean($groname);
- $this->fieldClean($user);
+ function dropFunction($function_oid, $cascade) {
+ // Function comes in with $object as function OID
+ $fn = $this->getFunction($function_oid);
+ $this->fieldClean($fn->fields['proname']);
- $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
+ $sql = "DROP FUNCTION \"{$this->_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
+ if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
}
@@ -3051,41 +4108,55 @@ class Postgres extends ADODB_base {
// Type functions
/**
+ * Returns all details for a particular type
+ * @param $typname The name of the view to retrieve
+ * @return Type info
+ */
+ function getType($typname) {
+ $this->clean($typname);
+
+ $sql = "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign
+ FROM pg_type WHERE typname='{$typname}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
* Returns a list of all types in the database
* @param $all If true, will find all available functions, if false just those in search path
* @param $tabletypes If true, will include table types
- * @param $domains Ignored
+ * @param $domains If true, will include domains
* @return A recordet
*/
function getTypes($all = false, $tabletypes = false, $domains = false) {
- global $conf;
-
- if ($all || $conf['show_system']) {
- $where = '';
- } else {
- $where = "AND pt.oid > '{$this->_lastSystemOID}'::oid";
- }
+ if ($all)
+ $where = '1 = 1';
+ else
+ $where = "n.nspname = '{$this->_schema}'";
// Never show system table types
- $where2 = "AND c.oid > '{$this->_lastSystemOID}'::oid";
+ $where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg@_%' ESCAPE '@')";
// Create type filter
$tqry = "'c'";
if ($tabletypes)
$tqry .= ", 'r', 'v'";
+ // Create domain filter
+ if (!$domains)
+ $where .= " AND t.typtype != 'd'";
+
$sql = "SELECT
- pt.typname AS basename,
- pt.typname,
+ t.typname AS basename,
+ pg_catalog.format_type(t.oid, NULL) AS typname,
pu.usename AS typowner,
- (SELECT description FROM pg_description pd WHERE pt.oid=pd.objoid) AS typcomment
- FROM
- pg_type pt,
- pg_user pu
- WHERE
- pt.typowner = pu.usesysid
- AND (pt.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_class c WHERE c.oid = pt.typrelid {$where2}))
- AND typname !~ '^_'
- {$where}
+ t.typtype,
+ pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment
+ FROM (pg_catalog.pg_type t
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace)
+ LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid
+ WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2}))
+ AND t.typname !~ '^_'
+ AND {$where}
ORDER BY typname
";
@@ -3093,26 +4164,12 @@ class Postgres extends ADODB_base {
}
/**
- * Returns all details for a particular type
- * @param $typname The name of the view to retrieve
- * @return Type info
- */
- function getType($typname) {
- $this->clean($typname);
-
- $sql = "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign
- FROM pg_type WHERE typname='{$typname}'";
-
- return $this->selectSet($sql);
- }
-
- /**
* Creates a new type
* @param ...
* @return 0 success
*/
function createType($typname, $typin, $typout, $typlen, $typdef,
- $typelem, $typdelim, $typbyval, $typalign, $typstorage) {
+ $typelem, $typdelim, $typbyval, $typalign, $typstorage) {
$this->fieldClean($typname);
$this->fieldClean($typin);
$this->fieldClean($typout);
@@ -3145,7 +4202,311 @@ class Postgres extends ADODB_base {
function dropType($typname, $cascade) {
$this->fieldClean($typname);
- $sql = "DROP TYPE \"{$typname}\"";
+ $sql = "DROP TYPE \"{$this->_schema}\".\"{$typname}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Creates a new enum type in the database
+ * @param $name The name of the type
+ * @param $values An array of values
+ * @param $typcomment Type comment
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 no values supplied
+ */
+ function createEnumType($name, $values, $typcomment) {
+ $this->fieldClean($name);
+ $this->clean($typcomment);
+
+ if (empty($values)) return -2;
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $values = array_unique($values);
+
+ $nbval = count($values);
+
+ for ($i = 0; $i < $nbval; $i++)
+ $this->clean($values[$i]);
+
+ $sql = "CREATE TYPE \"{$this->_schema}\".\"{$name}\" AS ENUM ('";
+ $sql.= implode("','", $values);
+ $sql .= "')";
+
+ $status = $this->execute($sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ if ($typcomment != '') {
+ $status = $this->setComment('TYPE', $name, '', $typcomment, true);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ return $this->endTransaction();
+
+ }
+
+ /**
+ * Get defined values for a given enum
+ * @return A recordset
+ */
+ function getEnumValues($name) {
+ $this->fieldClean($name);
+
+ $sql = "SELECT enumlabel AS enumval
+ FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON (t.oid=e.enumtypid)
+ WHERE t.typname = '{$name}' ORDER BY e.oid";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates a new composite type in the database
+ * @param $name The name of the type
+ * @param $fields The number of fields
+ * @param $field An array of field names
+ * @param $type An array of field types
+ * @param $array An array of '' or '[]' for each type if it's an array or not
+ * @param $length An array of field lengths
+ * @param $colcomment An array of comments
+ * @param $typcomment Type comment
+ * @return 0 success
+ * @return -1 no fields supplied
+ */
+ function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment) {
+ $this->fieldClean($name);
+ $this->clean($typcomment);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $found = false;
+ $first = true;
+ $comment_sql = ''; // Accumulate comments for the columns
+ $sql = "CREATE TYPE \"{$this->_schema}\".\"{$name}\" AS (";
+ for ($i = 0; $i < $fields; $i++) {
+ $this->fieldClean($field[$i]);
+ $this->clean($type[$i]);
+ $this->clean($length[$i]);
+ $this->clean($colcomment[$i]);
+
+ // Skip blank columns - for user convenience
+ if ($field[$i] == '' || $type[$i] == '') continue;
+ // If not the first column, add a comma
+ if (!$first) $sql .= ", ";
+ else $first = false;
+
+ switch ($type[$i]) {
+ // Have to account for weird placing of length for with/without
+ // time zone types
+ case 'timestamp with time zone':
+ case 'timestamp without time zone':
+ $qual = substr($type[$i], 9);
+ $sql .= "\"{$field[$i]}\" timestamp";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ $sql .= $qual;
+ break;
+ case 'time with time zone':
+ case 'time without time zone':
+ $qual = substr($type[$i], 4);
+ $sql .= "\"{$field[$i]}\" time";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ $sql .= $qual;
+ break;
+ default:
+ $sql .= "\"{$field[$i]}\" {$type[$i]}";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ }
+ // Add array qualifier if necessary
+ if ($array[$i] == '[]') $sql .= '[]';
+
+ if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$this->_schema}\".\"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
+
+ $found = true;
+ }
+
+ if (!$found) return -1;
+
+ $sql .= ")";
+
+ $status = $this->execute($sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ if ($typcomment != '') {
+ $status = $this->setComment('TYPE', $name, '', $typcomment, true);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ if ($comment_sql != '') {
+ $status = $this->execute($comment_sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+ return $this->endTransaction();
+ }
+
+ /**
+ * Returns a list of all casts in the database
+ * @return All casts
+ */
+ function getCasts() {
+ global $conf;
+
+ if ($conf['show_system'])
+ $where = '';
+ else
+ $where = "
+ AND n1.nspname NOT LIKE 'pg\\\\_%'
+ AND n2.nspname NOT LIKE 'pg\\\\_%'
+ AND n3.nspname NOT LIKE 'pg\\\\_%'
+ ";
+
+ $sql = "
+ SELECT
+ c.castsource::pg_catalog.regtype AS castsource,
+ c.casttarget::pg_catalog.regtype AS casttarget,
+ CASE WHEN c.castfunc=0 THEN NULL
+ ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc,
+ c.castcontext,
+ obj_description(c.oid, 'pg_cast') as castcomment
+ FROM
+ (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),
+ pg_catalog.pg_type t1,
+ pg_catalog.pg_type t2,
+ pg_catalog.pg_namespace n1,
+ pg_catalog.pg_namespace n2
+ WHERE
+ c.castsource=t1.oid
+ AND c.casttarget=t2.oid
+ AND t1.typnamespace=n1.oid
+ AND t2.typnamespace=n2.oid
+ {$where}
+ ORDER BY 1, 2
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns a list of all conversions in the database
+ * @return All conversions
+ */
+ function getConversions() {
+ $sql = "
+ SELECT
+ c.conname,
+ pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding,
+ pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding,
+ c.condefault,
+ pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment
+ FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n
+ WHERE n.oid = c.connamespace
+ AND n.nspname='{$this->_schema}'
+ ORDER BY 1;
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ // Rule functions
+
+ /**
+ * Returns a list of all rules on a table OR view
+ * @param $table The table to find rules for
+ * @return A recordset
+ */
+ function getRules($table) {
+ $this->clean($table);
+
+ $sql = "
+ SELECT *
+ FROM pg_catalog.pg_rules
+ WHERE
+ schemaname='{$this->_schema}' AND tablename='{$table}'
+ ORDER BY rulename
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Edits a rule on a table OR view
+ * @param $name The name of the new rule
+ * @param $event SELECT, INSERT, UPDATE or DELETE
+ * @param $table Table on which to create the rule
+ * @param $where When to execute the rule, '' indicates always
+ * @param $instead True if an INSTEAD rule, false otherwise
+ * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
+ * @param $action The action to take
+ * @return 0 success
+ * @return -1 invalid event
+ */
+ function setRule($name, $event, $table, $where, $instead, $type, $action) {
+ return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true);
+ }
+
+ /**
+ * Creates a rule
+ * @param $name The name of the new rule
+ * @param $event SELECT, INSERT, UPDATE or DELETE
+ * @param $table Table on which to create the rule
+ * @param $where When to execute the rule, '' indicates always
+ * @param $instead True if an INSTEAD rule, false otherwise
+ * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
+ * @param $action The action to take
+ * @param $replace (optional) True to replace existing rule, false otherwise
+ * @return 0 success
+ * @return -1 invalid event
+ */
+ function createRule($name, $event, $table, $where, $instead, $type, $action, $replace = false) {
+ $this->fieldClean($name);
+ $this->fieldClean($table);
+ if (!in_array($event, $this->rule_events)) return -1;
+
+ $schema = $this->schema();
+
+ $sql = "CREATE";
+ if ($replace) $sql .= " OR REPLACE";
+ $sql .= " RULE \"{$name}\" AS ON {$event} TO {$schema}\"{$table}\"";
+ // Can't escape WHERE clause
+ if ($where != '') $sql .= " WHERE {$where}";
+ $sql .= " DO";
+ if ($instead) $sql .= " INSTEAD";
+ if ($type == 'NOTHING')
+ $sql .= " NOTHING";
+ else $sql .= " ({$action})";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes a rule from a table OR view
+ * @param $rule The rule to drop
+ * @param $relation The relation from which to drop
+ * @param $cascade True to cascade drop, false to restrict
+ * @return 0 success
+ */
+ function dropRule($rule, $relation, $cascade) {
+ $this->fieldClean($rule);
+ $this->fieldClean($relation);
+
+ $sql = "DROP RULE \"{$rule}\" ON \"{$this->_schema}\".\"{$relation}\"";
if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
@@ -3154,6 +4515,52 @@ class Postgres extends ADODB_base {
// Trigger functions
/**
+ * Grabs a single trigger
+ * @param $table The name of a table whose triggers to retrieve
+ * @param $trigger The name of the trigger to retrieve
+ * @return A recordset
+ */
+ function getTrigger($table, $trigger) {
+ $this->clean($table);
+ $this->clean($trigger);
+
+ $sql = "
+ SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c
+ WHERE t.tgrelid=c.oid AND c.relname='{$table}' AND t.tgname='{$trigger}'
+ AND c.relnamespace=(
+ SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}')";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Grabs a list of triggers on a table
+ * @param $table The name of a table whose triggers to retrieve
+ * @return A recordset
+ */
+ function getTriggers($table = '') {
+ $this->clean($table);
+
+ $sql = "SELECT
+ t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
+ CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
+ p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
+ ns.nspname AS pronamespace
+ FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
+ WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
+ AND (NOT tgisconstraint OR NOT EXISTS
+ (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
+ AND p.oid=t.tgfoid
+ AND p.pronamespace = ns.oid";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
* A helper function for getTriggers that translates
* an array of attribute numbers to an array of field names.
* @param $trigger An array containing fields from the trigger table
@@ -3255,24 +4662,10 @@ class Postgres extends ADODB_base {
}
/**
- * Grabs a list of triggers on a table
- * @param $table The name of a table whose triggers to retrieve
- * @return A recordset
+ * Returns a list of all functions that can be used in triggers
*/
- function getTriggers($table = '') {
- $this->clean($table);
-
- // We include constraint triggers
- $sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
- t.tgargs, t.tgnargs, t.tgconstrrelid,
- (SELECT relname FROM pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
- (SELECT proname FROM pg_proc p WHERE t.tgfoid=p.oid) AS tgfname,
- c.relname, NULL AS tgdef
- FROM pg_trigger t, pg_class c
- WHERE t.tgrelid=c.oid
- AND c.relname='{$table}'";
-
- return $this->selectSet($sql);
+ function getTriggerFunctions() {
+ return $this->getFunctions(true, 'trigger');
}
/**
@@ -3292,13 +4685,30 @@ class Postgres extends ADODB_base {
/* No Statement Level Triggers in PostgreSQL (by now) */
$sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime}
- {$tgevent} ON \"{$table}\"
+ {$tgevent} ON \"{$this->_schema}\".\"{$table}\"
FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})";
return $this->execute($sql);
}
/**
+ * Alters a trigger
+ * @param $table The name of the table containing the trigger
+ * @param $trigger The name of the trigger to alter
+ * @param $name The new name for the trigger
+ * @return 0 success
+ */
+ function alterTrigger($table, $trigger, $name) {
+ $this->fieldClean($table);
+ $this->fieldClean($trigger);
+ $this->fieldClean($name);
+
+ $sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$this->_schema}\".\"{$table}\" RENAME TO \"{$name}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
* Drops a trigger
* @param $tgname The name of the trigger to drop
* @param $table The table from which to drop the trigger
@@ -3309,13 +4719,1429 @@ class Postgres extends ADODB_base {
$this->fieldClean($tgname);
$this->fieldClean($table);
- $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$table}\"";
+ $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$this->_schema}\".\"{$table}\"";
if ($cascade) $sql .= " CASCADE";
return $this->execute($sql);
}
- // Privilege functions
+ /**
+ * Enables a trigger
+ * @param $tgname The name of the trigger to enable
+ * @param $table The table in which to enable the trigger
+ * @return 0 success
+ */
+ function enableTrigger($tgname, $table) {
+ $this->fieldClean($tgname);
+ $this->fieldClean($table);
+
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ENABLE TRIGGER \"{$tgname}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Disables a trigger
+ * @param $tgname The name of the trigger to disable
+ * @param $table The table in which to disable the trigger
+ * @return 0 success
+ */
+ function disableTrigger($tgname, $table) {
+ $this->fieldClean($tgname);
+ $this->fieldClean($table);
+
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" DISABLE TRIGGER \"{$tgname}\"";
+
+ return $this->execute($sql);
+ }
+
+ // Operator functions
+
+ /**
+ * Returns a list of all operators in the database
+ * @return All operators
+ */
+ function getOperators() {
+ // We stick with the subselects here, as you cannot ORDER BY a regtype
+ $sql = "
+ SELECT
+ po.oid, po.oprname,
+ (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
+ (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
+ po.oprresult::pg_catalog.regtype AS resultname,
+ pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment
+ FROM
+ pg_catalog.pg_operator po
+ WHERE
+ po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
+ ORDER BY
+ po.oprname, oprleftname, oprrightname
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all details for a particular operator
+ * @param $operator_oid The oid of the operator
+ * @return Function info
+ */
+ function getOperator($operator_oid) {
+ $this->clean($operator_oid);
+
+ $sql = "
+ SELECT
+ po.oid, po.oprname,
+ oprleft::pg_catalog.regtype AS oprleftname,
+ oprright::pg_catalog.regtype AS oprrightname,
+ oprresult::pg_catalog.regtype AS resultname,
+ po.oprcanhash,
+ oprcom::pg_catalog.regoperator AS oprcom,
+ oprnegate::pg_catalog.regoperator AS oprnegate,
+ oprlsortop::pg_catalog.regoperator AS oprlsortop,
+ oprrsortop::pg_catalog.regoperator AS oprrsortop,
+ oprltcmpop::pg_catalog.regoperator AS oprltcmpop,
+ oprgtcmpop::pg_catalog.regoperator AS oprgtcmpop,
+ po.oprcode::pg_catalog.regproc AS oprcode,
+ po.oprrest::pg_catalog.regproc AS oprrest,
+ po.oprjoin::pg_catalog.regproc AS oprjoin
+ FROM
+ pg_catalog.pg_operator po
+ WHERE
+ po.oid='{$operator_oid}'
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Drops an operator
+ * @param $operator_oid The OID of the operator to drop
+ * @param $cascade True to cascade drop, false to restrict
+ * @return 0 success
+ */
+ function dropOperator($operator_oid, $cascade) {
+ // Function comes in with $object as operator OID
+ $opr = $this->getOperator($operator_oid);
+ $this->fieldClean($opr->fields['oprname']);
+
+ $schema = $this->schema();
+
+ $sql = "DROP OPERATOR {$schema}{$opr->fields['oprname']} (";
+ // Quoting or formatting here???
+ if ($opr->fields['oprleftname'] !== null) $sql .= $opr->fields['oprleftname'] . ', ';
+ else $sql .= "NONE, ";
+ if ($opr->fields['oprrightname'] !== null) $sql .= $opr->fields['oprrightname'] . ')';
+ else $sql .= "NONE)";
+
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ // Operator Class functions
+
+ /**
+ * Gets all opclasses
+ *
+ * @return A recordset
+ */
+
+ function getOpClasses() {
+
+ $sql = "
+ SELECT
+ pa.amname, po.opcname,
+ po.opcintype::pg_catalog.regtype AS opcintype,
+ po.opcdefault,
+ pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
+ FROM
+ pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
+ WHERE
+ po.opcmethod=pa.oid
+ AND po.opcnamespace=pn.oid
+ AND pn.nspname='{$this->_schema}'
+ ORDER BY 1,2
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ // FTS functions
+
+ /**
+ * Creates a new FTS configuration.
+ * @param string $cfgname The name of the FTS configuration to create
+ * @param string $parser The parser to be used in new FTS configuration
+ * @param string $locale Locale of the FTS configuration
+ * @param string $template The existing FTS configuration to be used as template for the new one
+ * @param string $withmap Should we copy whole map of existing FTS configuration to the new one
+ * @param string $makeDefault Should this configuration be the default for locale given
+ * @param string $comment If omitted, defaults to nothing
+ * @return 0 success
+ */
+ function createFtsConfiguration($cfgname, $parser = '', $template = '', $comment = '') {
+ $this->fieldClean($cfgname);
+
+ $sql = "CREATE TEXT SEARCH CONFIGURATION \"{$cfgname}\" (";
+ if ($parser != '') {
+ $this->fieldClean($parser['schema']);
+ $this->fieldClean($parser['parser']);
+ $parser = "\"{$parser['schema']}\".\"{$parser['parser']}\"";
+ $sql .= " PARSER = {$parser}";
+ }
+ if ($template != '') {
+ $this->fieldClean($template);
+ $sql .= " COPY = \"{$template}\"";
+ }
+ $sql .= ")";
+
+ if ($comment != '') {
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+ }
+
+ // Create the FTS configuration
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ // Set the comment
+ if ($comment != '') {
+ $this->clean($comment);
+ $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ return $this->endTransaction();
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns all FTS configurations available
+ */
+ function getFtsConfigurations() {
+ $sql = "
+ SELECT
+ n.nspname as schema,
+ c.cfgname as name,
+ pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
+ FROM
+ pg_catalog.pg_ts_config c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
+ WHERE
+ pg_catalog.pg_ts_config_is_visible(c.oid)
+ ORDER BY
+ schema, name";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Return all information related to a FTS configuration
+ * @param $ftscfg The name of the FTS configuration
+ * @return FTS configuration information
+ */
+ function getFtsConfigurationByName($ftscfg) {
+ $this->clean($ftscfg);
+ $sql = "
+ SELECT
+ n.nspname as schema,
+ c.cfgname as name,
+ p.prsname as parser,
+ c.cfgparser as parser_id,
+ pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
+ FROM pg_catalog.pg_ts_config c
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
+ LEFT JOIN pg_catalog.pg_ts_parser p ON p.oid = c.cfgparser
+ WHERE pg_catalog.pg_ts_config_is_visible(c.oid)
+ AND c.cfgname = '{$ftscfg}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns the map of FTS configuration given (list of mappings (tokens) and their processing dictionaries)
+ *
+ * @param string $ftscfg Name of the FTS configuration
+ */
+ function getFtsConfigurationMap($ftscfg) {
+ $this->fieldClean($ftscfg);
+ $getOidSql = "SELECT oid FROM pg_catalog.pg_ts_config WHERE cfgname = '{$ftscfg}'";
+ $oidSet = $this->selectSet($getOidSql);
+ $oid = $oidSet->fields['oid'];
+
+ $sql = "
+ SELECT
+ (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
+ (SELECT t.description FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS description,
+ c.cfgname AS cfgname, n.nspname ||'.'|| d.dictname as dictionaries
+ FROM
+ pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d,
+ pg_catalog.pg_namespace n
+ WHERE
+ c.oid = {$oid} AND m.mapcfg = c.oid and m.mapdict = d.oid and d.dictnamespace = n.oid
+ ORDER BY name
+ ";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all FTS parsers available
+ */
+ function getFtsParsers() {
+ $sql = "
+ SELECT
+ n.nspname as schema,
+ p.prsname as name,
+ pg_catalog.obj_description(p.oid, 'pg_ts_parser') as comment
+ FROM pg_catalog.pg_ts_parser p
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace
+ WHERE pg_catalog.pg_ts_parser_is_visible(p.oid)
+ ORDER BY schema, name";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all FTS dictionaries available
+ */
+ function getFtsDictionaries() {
+ $sql = "
+ SELECT
+ n.nspname as schema, d.dictname as name,
+ pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
+ FROM pg_catalog.pg_ts_dict d
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
+ WHERE pg_catalog.pg_ts_dict_is_visible(d.oid)
+ ORDER BY schema, name;";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all FTS dictionary templates available
+ */
+ function getFtsDictionaryTemplates() {
+ $sql = "
+ SELECT
+ n.nspname as schema,
+ t.tmplname as name,
+ ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname
+ FROM pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
+ WHERE t.tmplinit = p.oid ) AS init,
+ ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname
+ FROM pg_catalog.pg_proc p
+ LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
+ WHERE t.tmpllexize = p.oid ) AS lexize,
+ pg_catalog.obj_description(t.oid, 'pg_ts_template') as comment
+ FROM pg_catalog.pg_ts_template t
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace
+ WHERE pg_catalog.pg_ts_template_is_visible(t.oid)
+ ORDER BY schema, name;";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Drops FTS coniguration
+ */
+ function dropFtsConfiguration($ftscfg, $cascade) {
+ $this->fieldClean($ftscfg);
+
+ $sql = "DROP TEXT SEARCH CONFIGURATION \"{$ftscfg}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Drops FTS dictionary
+ *
+ * @todo Support of dictionary templates dropping
+ */
+ function dropFtsDictionary($ftsdict, $cascade = true) {
+ $this->fieldClean($ftsdict);
+
+ $sql = "DROP TEXT SEARCH DICTIONARY";
+ $sql .= " \"{$ftsdict}\"";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Alters FTS configuration
+ */
+ function updateFtsConfiguration($cfgname, $comment, $name) {
+ $this->fieldClean($cfgname);
+ $this->fieldClean($name);
+ $this->clean($comment);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ // Only if the name has changed
+ if ($name != $cfgname) {
+ $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$cfgname}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Creates a new FTS dictionary or FTS dictionary template.
+ * @param string $dictname The name of the FTS dictionary to create
+ * @param boolean $isTemplate Flag whether we create usual dictionary or dictionary template
+ * @param string $template The existing FTS dictionary to be used as template for the new one
+ * @param string $lexize The name of the function, which does transformation of input word
+ * @param string $init The name of the function, which initializes dictionary
+ * @param string $option Usually, it stores various options required for the dictionary
+ * @param string $comment If omitted, defaults to nothing
+ * @return 0 success
+ */
+ function createFtsDictionary($dictname, $isTemplate = false, $template = '', $lexize = '', $init = '', $option = '', $comment = '') {
+ $this->fieldClean($dictname);
+ $this->fieldClean($template);
+ $this->fieldClean($lexize);
+ $this->fieldClean($init);
+ $this->fieldClean($option);
+ $this->clean($comment);
+
+ $sql = "CREATE TEXT SEARCH";
+ if ($isTemplate) {
+ $sql .= " TEMPLATE {$dictname} (";
+ if ($lexize != '') $sql .= " LEXIZE = {$lexize}";
+ if ($init != '') $sql .= ", INIT = {$init}";
+ $sql .= ")";
+ $whatToComment = 'TEXT SEARCH TEMPLATE';
+ } else {
+ $sql .= " DICTIONARY {$dictname} (";
+ if ($template != '') $sql .= " TEMPLATE = {$template}";
+ if ($option != '') $sql .= ", {$option}";
+ $sql .= ")";
+ $whatToComment = 'TEXT SEARCH DICTIONARY';
+ }
+
+ if ($comment != '') {
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+ }
+
+ // Create the FTS dictionary
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ // Set the comment
+ if ($comment != '') {
+ $status = $this->setComment($whatToComment, $dictname, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Alters FTS dictionary or dictionary template
+ */
+ function updateFtsDictionary($dictname, $comment, $name) {
+ $this->fieldClean($dictname);
+ $this->fieldClean($name);
+ $this->clean($comment);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ $status = $this->setComment('TEXT SEARCH DICTIONARY', $dictname, '', $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ // Only if the name has changed
+ if ($name != $dictname) {
+ $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$dictname}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Return all information relating to a FTS dictionary
+ * @param $ftsdict The name of the FTS dictionary
+ * @return FTS dictionary information
+ */
+ function getFtsDictionaryByName($ftsdict) {
+ $this->clean($ftsdict);
+ $sql = "SELECT
+ n.nspname as schema,
+ d.dictname as name,
+ ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM
+ pg_catalog.pg_ts_template t
+ LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace
+ WHERE d.dicttemplate = t.oid ) AS template,
+ d.dictinitoption as init,
+ pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
+ FROM pg_catalog.pg_ts_dict d
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
+ WHERE d.dictname = '{$ftsdict}'
+ AND pg_catalog.pg_ts_dict_is_visible(d.oid)
+ ORDER BY schema, name";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates/updates/deletes FTS mapping.
+ * @param string $cfgname The name of the FTS configuration to alter
+ * @param array $mapping Array of tokens' names
+ * @param string $action What to do with the mapping: add, alter or drop
+ * @param string $dictname Dictionary that will process tokens given or null in case of drop action
+ * @return 0 success
+ */
+ function changeFtsMapping($ftscfg, $mapping, $action, $dictname = null) {
+ $this->fieldClean($ftscfg);
+ $this->fieldClean($dictname);
+ $this->arrayClean($mapping);
+
+ if (count($mapping) > 0) {
+ switch ($action) {
+ case 'alter':
+ $whatToDo = "ALTER";
+ break;
+ case 'drop':
+ $whatToDo = "DROP";
+ break;
+ default:
+ $whatToDo = "ADD";
+ break;
+ }
+ $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$ftscfg}\" {$whatToDo} MAPPING FOR ";
+ $sql .= implode(",", $mapping);
+ if ($action != 'drop' && !empty($dictname)) {
+ $sql .= " WITH {$dictname}";
+ }
+
+ $status = $this->beginTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ return $this->endTransaction();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Return all information related to a given FTS configuration's mapping
+ * @param $ftscfg The name of the FTS configuration
+ * @param $mapping The name of the mapping
+ * @return FTS configuration information
+ */
+ function getFtsMappingByName($ftscfg, $mapping) {
+ $this->fieldClean($ftscfg);
+ $this->fieldClean($mapping);
+
+ $getOidSql = "SELECT oid, cfgparser FROM pg_catalog.pg_ts_config WHERE cfgname = '{$ftscfg}'";
+ $oidSet = $this->selectSet($getOidSql);
+ $oid = $oidSet->fields['oid'];
+ $cfgparser = $oidSet->fields['cfgparser'];
+
+ $getTokenIdSql = "SELECT tokid FROM pg_catalog.ts_token_type({$cfgparser}) WHERE alias = '{$mapping}'";
+ $tokenIdSet = $this->selectSet($getTokenIdSql);
+ $tokid = $tokenIdSet->fields['tokid'];
+
+ $sql = "SELECT
+ (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
+ d.dictname as dictionaries
+ FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d
+ WHERE c.oid = {$oid} AND m.mapcfg = c.oid AND m.maptokentype = {$tokid} AND m.mapdict = d.oid
+ LIMIT 1;";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Return list of FTS mappings possible for given parser (specified by given configuration since configuration
+ * can only have 1 parser)
+ */
+ function getFtsMappings($ftscfg) {
+ $cfg = $this->getFtsConfigurationByName($ftscfg);
+ $sql = "SELECT alias AS name, description FROM pg_catalog.ts_token_type({$cfg->fields['parser_id']}) ORDER BY name";
+ return $this->selectSet($sql);
+ }
+
+ // Language functions
+
+ /**
+ * Gets all languages
+ * @param $all True to get all languages, regardless of show_system
+ * @return A recordset
+ */
+ function getLanguages($all = false) {
+ global $conf;
+
+ if ($conf['show_system'] || $all)
+ $where = '';
+ else
+ $where = 'WHERE lanispl';
+
+ $sql = "
+ SELECT
+ lanname, lanpltrusted,
+ lanplcallfoid::pg_catalog.regproc AS lanplcallf
+ FROM
+ pg_catalog.pg_language
+ {$where}
+ ORDER BY lanname
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+ // Aggregate functions
+
+ /**
+ * Creates a new aggregate in the database
+ * @param $name The name of the aggregate
+ * @param $basetype The input data type of the aggregate
+ * @param $sfunc The name of the state transition function for the aggregate
+ * @param $stype The data type for the aggregate's state value
+ * @param $ffunc The name of the final function for the aggregate
+ * @param $initcond The initial setting for the state value
+ * @param $sortop The sort operator for the aggregate
+ * @param $comment Aggregate comment
+ * @return 0 success
+ * @return -1 error
+ */
+ function createAggregate($name, $basetype, $sfunc, $stype, $ffunc, $initcond, $sortop, $comment) {
+ $this->fieldClean($name);
+ $this->fieldClean($basetype);
+ $this->fieldClean($sfunc);
+ $this->fieldClean($stype);
+ $this->fieldClean($ffunc);
+ $this->fieldClean($initcond);
+ $this->fieldClean($sortop);
+ $this->clean($comment);
+
+ $this->beginTransaction();
+
+ $schema = $this->schema();
+ $sql = "CREATE AGGREGATE {$schema}\"{$name}\" (BASETYPE = \"{$basetype}\", SFUNC = \"{$sfunc}\", STYPE = \"{$stype}\"";
+ if(trim($ffunc) != '') $sql .= ", FINALFUNC = \"{$ffunc}\"";
+ if(trim($initcond) != '') $sql .= ", INITCOND = \"{$initcond}\"";
+ if(trim($sortop) != '') $sql .= ", SORTOP = \"{$sortop}\"";
+ $sql .= ")";
+
+ $status = $this->execute($sql);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ if (trim($comment) != '') {
+ $status = $this->setComment('AGGREGATE', $name, '', $comment, $basetype);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Renames an aggregate function
+ * @param $aggrname The actual name of the aggregate
+ * @param $aggrtype The actual input data type of the aggregate
+ * @param $newaggrname The new name of the aggregate
+ * @return 0 success
+ */
+ function renameAggregate($aggrschema, $aggrname, $aggrtype, $newaggrname) {
+ $sql = "ALTER AGGREGATE \"{$aggrschema}\"" . '.' . "\"{$aggrname}\" (\"{$aggrtype}\") RENAME TO \"{$newaggrname}\"";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes an aggregate function from the database
+ * @param $aggrname The name of the aggregate
+ * @param $aggrtype The input data type of the aggregate
+ * @param $cascade True to cascade drop, false to restrict
+ * @return 0 success
+ */
+ function dropAggregate($aggrname, $aggrtype, $cascade) {
+ $this->fieldClean($aggrname);
+ $this->fieldClean($aggrtype);
+
+ $sql = "DROP AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\")";
+ if ($cascade) $sql .= " CASCADE";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Gets all information for an aggregate
+ * @param $name The name of the aggregate
+ * @param $basetype The input data type of the aggregate
+ * @return A recordset
+ */
+ function getAggregate($name, $basetype) {
+ $this->fieldclean($name);
+ $this->fieldclean($basetype);
+
+ $sql = "
+ SELECT p.proname, CASE p.proargtypes[0]
+ WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL
+ ELSE pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes,
+ a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn,
+ a.agginitval, a.aggsortop, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
+ FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
+ WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
+ AND p.proisagg AND n.nspname='{$this->_schema}'
+ AND p.proname='" . $name . "'
+ AND CASE p.proargtypes[0]
+ WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN ''
+ ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
+ END ='" . $basetype . "'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Gets all aggregates
+ * @return A recordset
+ */
+ function getAggregates() {
+ $sql = "SELECT p.proname, CASE p.proargtypes[0] WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL ELSE
+ pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes, a.aggtransfn, u.usename,
+ pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
+ FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
+ WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
+ AND p.proisagg AND n.nspname='{$this->_schema}' ORDER BY 1, 2";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Changes the owner of an aggregate function
+ * @param $aggrname The name of the aggregate
+ * @param $aggrtype The input data type of the aggregate
+ * @param $newaggrowner The new owner of the aggregate
+ * @return 0 success
+ */
+ function changeAggregateOwner($aggrname, $aggrtype, $newaggrowner) {
+ $sql = "ALTER AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") OWNER TO \"{$newaggrowner}\"";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Changes the schema of an aggregate function
+ * @param $aggrname The name of the aggregate
+ * @param $aggrtype The input data type of the aggregate
+ * @param $newaggrschema The new schema for the aggregate
+ * @return 0 success
+ */
+ function changeAggregateSchema($aggrname, $aggrtype, $newaggrschema) {
+ $sql = "ALTER AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") SET SCHEMA \"{$newaggrschema}\"";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Alters an aggregate
+ * @param $aggrname The actual name of the aggregate
+ * @param $aggrtype The actual input data type of the aggregate
+ * @param $aggrowner The actual owner of the aggregate
+ * @param $aggrschema The actual schema the aggregate belongs to
+ * @param $aggrcomment The actual comment for the aggregate
+ * @param $newaggrname The new name of the aggregate
+ * @param $newaggrowner The new owner of the aggregate
+ * @param $newaggrschema The new schema where the aggregate will belong to
+ * @param $newaggrcomment The new comment for the aggregate
+ * @return 0 success
+ * @return -1 change owner error
+ * @return -2 change comment error
+ * @return -3 change schema error
+ * @return -4 change name error
+ */
+ function alterAggregate($aggrname, $aggrtype, $aggrowner, $aggrschema, $aggrcomment, $newaggrname, $newaggrowner, $newaggrschema, $newaggrcomment) {
+ // Clean fields
+ $this->fieldClean($aggrname);
+ $this->fieldClean($aggrtype);
+ $this->fieldClean($aggrowner);
+ $this->fieldClean($aggrschema);
+ $this->clean($aggrcomment);
+ $this->fieldClean($newaggrname);
+ $this->fieldClean($newaggrowner);
+ $this->fieldClean($newaggrschema);
+ $this->clean($newaggrcomment);
+
+ $this->beginTransaction();
+
+ // Change the owner, if it has changed
+ if($aggrowner != $newaggrowner) {
+ $status = $this->changeAggregateOwner($aggrname, $aggrtype, $newaggrowner);
+ if($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ }
+
+ // Set the comment, if it has changed
+ if($aggrcomment != $newaggrcomment) {
+ $status = $this->setComment('AGGREGATE', $aggrname, '', $newaggrcomment, $aggrtype);
+ if ($status) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+ }
+
+ // Change the schema, if it has changed
+ if($aggrschema != $newaggrschema) {
+ $status = $this->changeAggregateSchema($aggrname, $aggrtype, $newaggrschema);
+ if($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+ }
+
+ // Rename the aggregate, if it has changed
+ if($aggrname != $newaggrname) {
+ $status = $this->renameAggregate($newaggrschema, $aggrname, $aggrtype, $newaggrname);
+ if($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ // Role, User/Group functions
+
+ /**
+ * Returns all roles in the database cluster
+ * @param $rolename (optional) The role name to exclude from the select
+ * @return All roles
+ */
+ function getRoles($rolename = '') {
+ $sql = '
+ SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
+ rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
+ FROM pg_catalog.pg_roles';
+ if($rolename) $sql .= " WHERE rolname!='{$rolename}'";
+ $sql .= ' ORDER BY rolname';
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns information about a single role
+ * @param $rolename The name of the role to retrieve
+ * @return The role's data
+ */
+ function getRole($rolename) {
+ $this->clean($rolename);
+
+ $sql = "
+ SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit,
+ rolcanlogin, rolconnlimit, rolvaliduntil, rolconfig
+ FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Grants membership in a role
+ * @param $role The name of the target role
+ * @param $rolename The name of the role that will belong to the target role
+ * @param $admin (optional) Flag to grant the admin option
+ * @return 0 success
+ */
+ function grantRole($role, $rolename, $admin=0) {
+ $this->fieldClean($role);
+ $this->fieldClean($rolename);
+
+ $sql = "GRANT \"{$role}\" TO \"{$rolename}\"";
+ if($admin == 1) $sql .= ' WITH ADMIN OPTION';
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Revokes membership in a role
+ * @param $role The name of the target role
+ * @param $rolename The name of the role that will not belong to the target role
+ * @param $admin (optional) Flag to revoke only the admin option
+ * @param $type (optional) Type of revoke: RESTRICT | CASCADE
+ * @return 0 success
+ */
+ function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT') {
+ $this->fieldClean($role);
+ $this->fieldClean($rolename);
+
+ $sql = "REVOKE ";
+ if($admin == 1) $sql .= 'ADMIN OPTION FOR ';
+ $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Returns all users in the database cluster
+ * @return All users
+ */
+ function getUsers() {
+ $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
+ if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
+ $sql .= " FROM pg_user ORDER BY usename";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns information about a single user
+ * @param $username The username of the user to retrieve
+ * @return The user's data
+ */
+ function getUser($username) {
+ $this->clean($username);
+
+ $sql = "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
+ if ($this->hasUserSessionDefaults()) $sql .= ", useconfig";
+ $sql .= " FROM pg_user WHERE usename='{$username}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates a new role
+ * @param $rolename The name of the role to create
+ * @param $password A password for the role
+ * @param $superuser Boolean whether or not the role is a superuser
+ * @param $createdb Boolean whether or not the role can create databases
+ * @param $createrole Boolean whether or not the role can create other roles
+ * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
+ * @param $login Boolean whether or not the role will be allowed to login
+ * @param $connlimit Number of concurrent connections the role can make
+ * @param $expiry String Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
+ * @param $memberof (array) Roles to which the new role will be immediately added as a new member
+ * @param $members (array) Roles which are automatically added as members of the new role
+ * @param $adminmembers (array) Roles which are automatically added as admin members of the new role
+ * @return 0 success
+ */
+ function createRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers) {
+ $enc = $this->_encryptPassword($rolename, $password);
+ $this->fieldClean($rolename);
+ $this->clean($enc);
+ $this->clean($connlimit);
+ $this->clean($expiry);
+ $this->fieldArrayClean($memberof);
+ $this->fieldArrayClean($members);
+ $this->fieldArrayClean($adminmembers);
+
+ $sql = "CREATE ROLE \"{$rolename}\"";
+ if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
+ $sql .= ($superuser) ? ' SUPERUSER' : ' NOSUPERUSER';
+ $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
+ $sql .= ($createrole) ? ' CREATEROLE' : ' NOCREATEROLE';
+ $sql .= ($inherits) ? ' INHERIT' : ' NOINHERIT';
+ $sql .= ($login) ? ' LOGIN' : ' NOLOGIN';
+ if ($connlimit != '') $sql .= " CONNECTION LIMIT {$connlimit}"; else $sql .= ' CONNECTION LIMIT -1';
+ if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'"; else $sql .= " VALID UNTIL 'infinity'";
+ if (is_array($memberof) && sizeof($memberof) > 0) $sql .= ' IN ROLE "' . join('", "', $memberof) . '"';
+ if (is_array($members) && sizeof($members) > 0) $sql .= ' ROLE "' . join('", "', $members) . '"';
+ if (is_array($adminmembers) && sizeof($adminmembers) > 0) $sql .= ' ADMIN "' . join('", "', $adminmembers) . '"';
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Adjusts a role's info
+ * @param $rolename The name of the role to adjust
+ * @param $password A password for the role
+ * @param $superuser Boolean whether or not the role is a superuser
+ * @param $createdb Boolean whether or not the role can create databases
+ * @param $createrole Boolean whether or not the role can create other roles
+ * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
+ * @param $login Boolean whether or not the role will be allowed to login
+ * @param $connlimit Number of concurrent connections the role can make
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
+ * @param $memberof (array) Roles to which the role will be immediately added as a new member
+ * @param $members (array) Roles which are automatically added as members of the role
+ * @param $adminmembers (array) Roles which are automatically added as admin members of the role
+ * @param $memberofold (array) Original roles whose the role belongs to
+ * @param $membersold (array) Original roles that are members of the role
+ * @param $adminmembersold (array) Original roles that are admin members of the role
+ * @return 0 success
+ */
+ function setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers, $memberofold, $membersold, $adminmembersold) {
+ $enc = $this->_encryptPassword($rolename, $password);
+ $this->fieldClean($rolename);
+ $this->clean($enc);
+ $this->clean($connlimit);
+ $this->clean($expiry);
+ $this->fieldArrayClean($memberof);
+ $this->fieldArrayClean($members);
+ $this->fieldArrayClean($adminmembers);
+
+ $sql = "ALTER ROLE \"{$rolename}\"";
+ if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
+ $sql .= ($superuser) ? ' SUPERUSER' : ' NOSUPERUSER';
+ $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
+ $sql .= ($createrole) ? ' CREATEROLE' : ' NOCREATEROLE';
+ $sql .= ($inherits) ? ' INHERIT' : ' NOINHERIT';
+ $sql .= ($login) ? ' LOGIN' : ' NOLOGIN';
+ if ($connlimit != '') $sql .= " CONNECTION LIMIT {$connlimit}"; else $sql .= ' CONNECTION LIMIT -1';
+ if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'"; else $sql .= " VALID UNTIL 'infinity'";
+
+ $status = $this->execute($sql);
+
+ if ($status != 0) return -1;
+
+ //memberof
+ $old = explode(',', $memberofold);
+ foreach ($memberof as $m) {
+ if (!in_array($m, $old)) {
+ $status = $this->grantRole($m, $rolename);
+ if ($status != 0) return -1;
+ }
+ }
+ if($memberofold)
+ {
+ foreach ($old as $o) {
+ if (!in_array($o, $memberof)) {
+ $status = $this->revokeRole($o, $rolename, 0, 'CASCADE');
+ if ($status != 0) return -1;
+ }
+ }
+ }
+
+ //members
+ $old = explode(',', $membersold);
+ foreach ($members as $m) {
+ if (!in_array($m, $old)) {
+ $status = $this->grantRole($rolename, $m);
+ if ($status != 0) return -1;
+ }
+ }
+ if($membersold)
+ {
+ foreach ($old as $o) {
+ if (!in_array($o, $members)) {
+ $status = $this->revokeRole($rolename, $o, 0, 'CASCADE');
+ if ($status != 0) return -1;
+ }
+ }
+ }
+
+ //adminmembers
+ $old = explode(',', $adminmembersold);
+ foreach ($adminmembers as $m) {
+ if (!in_array($m, $old)) {
+ $status = $this->grantRole($rolename, $m, 1);
+ if ($status != 0) return -1;
+ }
+ }
+ if($adminmembersold)
+ {
+ foreach ($old as $o) {
+ if (!in_array($o, $adminmembers)) {
+ $status = $this->revokeRole($rolename, $o, 1, 'CASCADE');
+ if ($status != 0) return -1;
+ }
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Renames a role
+ * @param $rolename The name of the role to rename
+ * @param $newrolename The new name of the role
+ * @return 0 success
+ */
+ function renameRole($rolename, $newrolename){
+ $this->fieldClean($rolename);
+ $this->fieldClean($newrolename);
+
+ $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Adjusts a role's info and renames it
+ * @param $rolename The name of the role to adjust
+ * @param $password A password for the role
+ * @param $superuser Boolean whether or not the role is a superuser
+ * @param $createdb Boolean whether or not the role can create databases
+ * @param $createrole Boolean whether or not the role can create other roles
+ * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
+ * @param $login Boolean whether or not the role will be allowed to login
+ * @param $connlimit Number of concurrent connections the role can make
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
+ * @param $memberof (array) Roles to which the role will be immediately added as a new member
+ * @param $members (array) Roles which are automatically added as members of the role
+ * @param $adminmembers (array) Roles which are automatically added as admin members of the role
+ * @param $memberofold (array) Original roles whose the role belongs to
+ * @param $membersold (array) Original roles that are members of the role
+ * @param $adminmembersold (array) Original roles that are admin members of the role
+ * @param $newrolename The new name of the role
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 set role attributes error
+ * @return -3 rename error
+ */
+ function setRenameRole($rolename, $password, $superuser, $createdb, $createrole,
+ $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers,
+ $memberofold, $membersold, $adminmembersold, $newrolename) {
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $status = $this->setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers, $memberofold, $membersold, $adminmembersold);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+
+ if ($rolename != $newrolename){
+ $status = $this->renameRole($rolename, $newrolename);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Removes a role
+ * @param $rolename The name of the role to drop
+ * @return 0 success
+ */
+ function dropRole($rolename) {
+ $this->fieldClean($rolename);
+
+ $sql = "DROP ROLE \"{$rolename}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Creates a new user
+ * @param $username The username of the user to create
+ * @param $password A password for the user
+ * @param $createdb boolean Whether or not the user can create databases
+ * @param $createuser boolean Whether or not the user can create other users
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
+ * @param $group (array) The groups to create the user in
+ * @return 0 success
+ */
+ function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {
+ $enc = $this->_encryptPassword($username, $password);
+ $this->fieldClean($username);
+ $this->clean($enc);
+ $this->clean($expiry);
+ $this->fieldArrayClean($groups);
+
+ $sql = "CREATE USER \"{$username}\"";
+ if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
+ $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
+ $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
+ if (is_array($groups) && sizeof($groups) > 0) $sql .= " IN GROUP \"" . join('", "', $groups) . "\"";
+ if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
+ else $sql .= " VALID UNTIL 'infinity'";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Renames a user
+ * @param $username The username of the user to rename
+ * @param $newname The new name of the user
+ * @return 0 success
+ */
+ function renameUser($username, $newname){
+ $this->fieldClean($username);
+ $this->fieldClean($newname);
+
+ $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Adjusts a user's info
+ * @param $username The username of the user to modify
+ * @param $password A new password for the user
+ * @param $createdb boolean Whether or not the user can create databases
+ * @param $createuser boolean Whether or not the user can create other users
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
+ * @return 0 success
+ */
+ function setUser($username, $password, $createdb, $createuser, $expiry) {
+ $enc = $this->_encryptPassword($username, $password);
+ $this->fieldClean($username);
+ $this->clean($enc);
+ $this->clean($expiry);
+
+ $sql = "ALTER USER \"{$username}\"";
+ if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
+ $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
+ $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
+ if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
+ else $sql .= " VALID UNTIL 'infinity'";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Adjusts a user's info and renames the user
+ * @param $username The username of the user to modify
+ * @param $password A new password for the user
+ * @param $createdb boolean Whether or not the user can create databases
+ * @param $createuser boolean Whether or not the user can create other users
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
+ * @param $newname The new name of the user
+ * @return 0 success
+ * @return -1 transaction error
+ * @return -2 set user attributes error
+ * @return -3 rename error
+ */
+ function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname) {
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $status = $this->setUser($username, $password, $createdb, $createuser, $expiry);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+
+ if ($username != $newname){
+ $status = $this->renameUser($username, $newname);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * Removes a user
+ * @param $username The username of the user to drop
+ * @return 0 success
+ */
+ function dropUser($username) {
+ $this->fieldClean($username);
+
+ $sql = "DROP USER \"{$username}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Determines whether or not a user is a super user
+ * @param $username The username of the user
+ * @return True if is a super user, false otherwise
+ */
+ function isSuperUser($username) {
+ $this->clean($username);
+
+ if (function_exists('pg_parameter_status')) {
+ $val = pg_parameter_status($this->conn->_connectionID, 'is_superuser');
+ if ($val !== false) return $val == 'on';
+ }
+
+ $sql = "SELECT usesuper FROM pg_user WHERE usename='{$username}'";
+
+ $usesuper = $this->selectField($sql, 'usesuper');
+ if ($usesuper == -1) return false;
+ else return $usesuper == 't';
+ }
+
+ /**
+ * Changes a role's password
+ * @param $rolename The role name
+ * @param $password The new password
+ * @return 0 success
+ */
+ function changePassword($rolename, $password) {
+ $enc = $this->_encryptPassword($rolename, $password);
+ $this->fieldClean($rolename);
+ $this->clean($enc);
+
+ $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Adds a group member
+ * @param $groname The name of the group
+ * @param $user The name of the user to add to the group
+ * @return 0 success
+ */
+ function addGroupMember($groname, $user) {
+ $this->fieldClean($groname);
+ $this->fieldClean($user);
+
+ $sql = "ALTER GROUP \"{$groname}\" ADD USER \"{$user}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Returns all role names which the role belongs to
+ * @param $rolename The role name
+ * @return All role names
+ */
+ function getMemberOf($rolename) {
+ $this->clean($rolename);
+
+ $sql = "
+ SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
+ WHERE R.oid=M.roleid
+ AND member IN (
+ SELECT oid FROM pg_catalog.pg_roles
+ WHERE rolname='{$rolename}')
+ ORDER BY rolname";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all role names that are members of a role
+ * @param $rolename The role name
+ * @param $admin (optional) Find only admin members
+ * @return All role names
+ */
+ function getMembers($rolename, $admin = 'f') {
+ $this->clean($rolename);
+
+ $sql = "
+ SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M
+ WHERE R.oid=M.member AND admin_option='{$admin}'
+ AND roleid IN (SELECT oid FROM pg_catalog.pg_roles
+ WHERE rolname='{$rolename}')
+ ORDER BY rolname";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Removes a group member
+ * @param $groname The name of the group
+ * @param $user The name of the user to remove from the group
+ * @return 0 success
+ */
+ function dropGroupMember($groname, $user) {
+ $this->fieldClean($groname);
+ $this->fieldClean($user);
+
+ $sql = "ALTER GROUP \"{$groname}\" DROP USER \"{$user}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Return users in a specific group
+ * @param $groname The name of the group
+ * @return All users in the group
+ */
+ function getGroup($groname) {
+ $this->clean($groname);
+
+ $sql = "
+ SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g
+ WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist)
+ ORDER BY s.usename";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Returns all groups in the database cluser
+ * @return All groups
+ */
+ function getGroups() {
+ $sql = "SELECT groname FROM pg_group ORDER BY groname";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates a new group
+ * @param $groname The name of the group
+ * @param $users An array of users to add to the group
+ * @return 0 success
+ */
+ function createGroup($groname, $users) {
+ $this->fieldClean($groname);
+
+ $sql = "CREATE GROUP \"{$groname}\"";
+
+ if (is_array($users) && sizeof($users) > 0) {
+ $this->fieldArrayClean($users);
+ $sql .= ' WITH USER "' . join('", "', $users) . '"';
+ }
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes a group
+ * @param $groname The name of the group to drop
+ * @return 0 success
+ */
+ function dropGroup($groname) {
+ $this->fieldClean($groname);
+
+ $sql = "DROP GROUP \"{$groname}\"";
+
+ return $this->execute($sql);
+ }
/**
* Internal function used for parsing ACLs
@@ -3445,7 +6271,7 @@ class Postgres extends ADODB_base {
* Grabs an array of users and their privileges for an object,
* given its type.
* @param $object The name of the object whose privileges are to be retrieved
- * @param $type The type of the object (eg. relation, view or sequence)
+ * @param $type The type of the object (eg. database, schema, relation, function or language)
* @return Privileges array
* @return -1 invalid type
* @return -2 object not found
@@ -3458,7 +6284,28 @@ class Postgres extends ADODB_base {
case 'table':
case 'view':
case 'sequence':
- $sql = "SELECT relacl AS acl FROM pg_class WHERE relname='{$object}'";
+ $sql = "
+ SELECT relacl AS acl FROM pg_catalog.pg_class
+ WHERE relname='{$object}'
+ AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}')";
+ break;
+ case 'database':
+ $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
+ break;
+ case 'function':
+ // Since we fetch functions by oid, they are already constrained to
+ // the current schema.
+ $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
+ break;
+ case 'language':
+ $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
+ break;
+ case 'schema':
+ $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
+ break;
+ case 'tablespace':
+ $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
break;
default:
return -1;
@@ -3585,519 +6432,270 @@ class Postgres extends ADODB_base {
return $this->execute($sql);
}
- // Administration functions
-
/**
- * Vacuums a database
- * @param $table The table to vacuum
- * @param $analyze If true, also does analyze
- * @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
- * @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
+ * Helper function that computes encypted PostgreSQL passwords
+ * @param $username The username
+ * @param $password The password
*/
- function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) {
- $sql = "VACUUM";
- if ($analyze) $sql .= " ANALYZE";
- if ($table != '') {
- $this->fieldClean($table);
- $sql .= " \"{$table}\"";
+ function _encryptPassword($username, $password) {
+ return 'md5' . md5($password . $username);
}
- return $this->execute($sql);
- }
+ // Tablespace functions
/**
- * Analyze a database
- * @param $table (optional) The table to analyze
+ * Retrieves information for all tablespaces
+ * @param $all Include all tablespaces (necessary when moving objects back to the default space)
+ * @return A recordset
*/
- function analyzeDB($table = '') {
- if ($table != '') {
- $this->fieldClean($table);
- $sql = "VACUUM ANALYZE \"{$table}\"";
- }
- else
- $sql = "VACUUM ANALYZE";
+ function getTablespaces($all = false) {
+ global $conf;
- return $this->execute($sql);
- }
+ $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation,
+ (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid) AS spccomment
+ FROM pg_catalog.pg_tablespace";
- /**
- * Rebuild indexes
- * @param $type 'DATABASE' or 'TABLE' or 'INDEX'
- * @param $name The name of the specific database, table, or index to be reindexed
- * @param $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in 7.2-7.3, ignored in >=7.4
- */
- function reindex($type, $name, $force = false) {
- $this->fieldClean($name);
- switch($type) {
- case 'DATABASE':
- case 'TABLE':
- case 'INDEX':
- $sql = "REINDEX {$type} \"{$name}\"";
- if ($force) $sql .= ' FORCE';
- break;
- default:
- return -1;
+ if (!$conf['show_system'] && !$all) {
+ $sql .= " WHERE spcname NOT LIKE 'pg\\\\_%'";
}
- return $this->execute($sql);
- }
+ $sql .= " ORDER BY spcname";
- // Function functions
+ return $this->selectSet($sql);
+ }
/**
- * Returns a list of all functions in the database
- * @param $all If true, will find all available functions, if false just userland ones
- * @return All functions
+ * Retrieves a tablespace's information
+ * @return A recordset
*/
- function getFunctions($all = false) {
- global $conf;
+ function getTablespace($spcname) {
+ $this->clean($spcname);
- if ($all || $conf['show_system'])
- $where = '';
- else
- $where = "AND pc.oid > '{$this->_lastSystemOID}'::oid";
-
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- proretset,
- pt.typname AS proresult,
- pl.lanname AS prolanguage,
- oidvectortypes(pc.proargtypes) AS proarguments,
- (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
- proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
- CASE WHEN proretset THEN 'setof '::text ELSE '' END || pt.typname AS proreturns,
- usename as proowner
- FROM
- pg_proc pc, pg_user pu, pg_type pt, pg_language pl
- WHERE
- pc.proowner = pu.usesysid
- AND pc.prorettype = pt.oid
- AND pc.prolang = pl.oid
- {$where}
- UNION
- SELECT
- pc.oid AS prooid,
- proname,
- proretset,
- 'opaque' AS proresult,
- pl.lanname AS prolanguage,
- oidvectortypes(pc.proargtypes) AS proarguments,
- (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
- proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
- CASE WHEN proretset THEN 'setof '::text ELSE '' END || 'opaque' AS proreturns,
- usename as proowner
- FROM
- pg_proc pc, pg_user pu, pg_type pt, pg_language pl
- WHERE
- pc.proowner = pu.usesysid
- AND pc.prorettype = 0
- AND pc.prolang = pl.oid
- {$where}
- ORDER BY
- proname, proresult
- ";
+ $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation,
+ (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid) AS spccomment
+ FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
return $this->selectSet($sql);
}
/**
- * Returns a list of all functions that can be used in triggers
- */
- function getTriggerFunctions() {
- return $this->getFunctions(true);
- }
-
- /**
- * Returns all details for a particular function
- * @param $function_oid The OID of the function to retrieve
- * @return Function info
+ * Creates a tablespace
+ * @param $spcname The name of the tablespace to create
+ * @param $spcowner The owner of the tablespace. '' for current
+ * @param $spcloc The directory in which to create the tablespace
+ * @return 0 success
*/
- function getFunction($function_oid) {
- $this->clean($function_oid);
+ function createTablespace($spcname, $spcowner, $spcloc, $comment='') {
+ $this->fieldClean($spcname);
+ $this->clean($spcloc);
+ $this->clean($comment);
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- lanname AS prolanguage,
- pt.typname AS proresult,
- prosrc,
- probin,
- proretset,
- proiscachable,
- oidvectortypes(pc.proargtypes) AS proarguments,
- (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment
- FROM
- pg_proc pc, pg_language pl, pg_type pt
- WHERE
- pc.oid = '$function_oid'::oid
- AND pc.prolang = pl.oid
- AND pc.prorettype = pt.oid
- ";
+ $sql = "CREATE TABLESPACE \"{$spcname}\"";
- return $this->selectSet($sql);
+ if ($spcowner != '') {
+ $this->fieldClean($spcowner);
+ $sql .= " OWNER \"{$spcowner}\"";
}
- /**
- * Returns an array containing a function's properties
- * @param $f The array of data for the function
- * @return An array containing the properties
- */
- function getFunctionProperties($f) {
- $temp = array();
+ $sql .= " LOCATION '{$spcloc}'";
- // Cachable
- $f['proiscachable'] = $this->phpBool($f['proiscachable']);
- if ($f['proiscachable'])
- $temp[] = 'ISCACHABLE';
- else
- $temp[] = '';
+ $status = $this->execute($sql);
+ if ($status != 0) return -1;
- return $temp;
+ if ($comment != '' && $this->hasSharedComments()) {
+ $status = $this->setComment('TABLESPACE',$spcname,'',$comment);
+ if ($status != 0) return -2;
+ }
+
+ return 0;
}
/**
- * Updates a function. Postgres 7.1 doesn't have CREATE OR REPLACE function,
- * so we do it with a drop and a recreate.
- * @param $function_oid The OID of the function
- * @param $funcname The name of the function to create
- * @param $newname The new name for the function
- * @param $args The array of argument types
- * @param $returns The return type
- * @param $definition The definition for the new function
- * @param $language The language the function is written for
- * @param $flags An array of optional flags
- * @param $setof True if returns a set, false otherwise
- * @param $comment The comment on the function
+ * Alters a tablespace
+ * @param $spcname The name of the tablespace
+ * @param $name The new name for the tablespace
+ * @param $owner The new owner for the tablespace
* @return 0 success
* @return -1 transaction error
- * @return -2 drop function error
- * @return -3 create function error
+ * @return -2 owner error
+ * @return -3 rename error
* @return -4 comment error
*/
- function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $comment) {
+ function alterTablespace($spcname, $name, $owner, $comment='') {
+ $this->fieldClean($spcname);
+ $this->fieldClean($name);
+ $this->fieldClean($owner);
+
+ // Begin transaction
$status = $this->beginTransaction();
if ($status != 0) return -1;
- // Drop existing function
- $status = $this->dropFunction($function_oid, false);
+ // Owner
+ $sql = "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\"";
+ $status = $this->execute($sql);
if ($status != 0) {
$this->rollbackTransaction();
return -2;
}
- // Create function with new name
- $status = $this->createFunction($newname, $args, $returns, $definition, $language, $flags, $setof, false);
+ // Rename (only if name has changed)
+ if ($name != $spcname) {
+ $sql = "ALTER TABLESPACE \"{$spcname}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
if ($status != 0) {
$this->rollbackTransaction();
return -3;
}
+ }
- // Comment on the function
- $this->fieldClean($newname);
- $this->clean($comment);
-
- $status = $this->setComment('FUNCTION', "\"{$newname}\"({$args})", null, $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
+ // Set comment if it has changed
+ if (trim($comment) != '' && $this->hasSharedComments()) {
+ $status = $this->setComment('TABLESPACE',$spcname,'',$comment);
+ if ($status != 0) return -4;
}
- $status = $this->endTransaction();
- return ($status == 0) ? 0 : -1;
+ return $this->endTransaction();
}
/**
- * Creates a new function.
- * @param $funcname The name of the function to create
- * @param $args A comma separated string of types
- * @param $returns The return type
- * @param $definition The definition for the new function
- * @param $language The language the function is written for
- * @param $flags An array of optional flags
- * @param $setof True if it returns a set, false otherwise
- * @param $rows number of rows planner should estimate will be returned
- * @param $cost cost the planner should use in the function execution step
- * @param $replace (optional) True if OR REPLACE, false for normal
+ * Drops a tablespace
+ * @param $spcname The name of the domain to drop
* @return 0 success
*/
- function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $replace = false) {
- $this->fieldClean($funcname);
- $this->clean($args);
- $this->clean($language);
- $this->arrayClean($flags);
-
- $sql = "CREATE";
- if ($replace) $sql .= " OR REPLACE";
- $sql .= " FUNCTION \"{$funcname}\" (";
-
- if ($args != '')
- $sql .= $args;
-
- // For some reason, the returns field cannot have quotes...
- $sql .= ") RETURNS ";
- if ($setof) $sql .= "SETOF ";
- $sql .= "{$returns} AS ";
-
- if (is_array($definition)) {
- $this->arrayClean($definition);
- $sql .= "'" . $definition[0] . "'";
- if ($definition[1]) {
- $sql .= ",'" . $definition[1] . "'";
- }
- } else {
- $this->clean($definition);
- $sql .= "'" . $definition . "'";
- }
+ function dropTablespace($spcname) {
+ $this->fieldClean($spcname);
- $sql .= " LANGUAGE '{$language}'";
+ $sql = "DROP TABLESPACE \"{$spcname}\"";
- // Add flags
- $first = true;
- foreach ($flags as $v) {
- // Skip default flags
- if ($v == '') continue;
- elseif ($first) {
- $sql .= " WITH ({$v}";
- $first = false;
- }
- else {
- $sql .= ", {$v}";
- }
+ return $this->execute($sql);
}
- // Close off WITH clause if necessary
- if (!$first) $sql .= ")";
- return $this->execute($sql);
- }
+ // Administration functions
/**
- * Drops a function.
- * @param $function_oid The OID of the function to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
+ * Analyze a database
+ * @note PostgreSQL 7.2 finally had an independent ANALYZE command
+ * @param $table (optional) The table to analyze
*/
- function dropFunction($function_oid, $cascade) {
- // Function comes in with $object as function OID
- $fn = $this->getFunction($function_oid);
- $this->fieldClean($fn->fields['proname']);
+ function analyzeDB($table = '') {
+ if ($table != '') {
+ $this->fieldClean($table);
- $sql = "DROP FUNCTION \"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
- if ($cascade) $sql .= " CASCADE";
+ $sql = "ANALYZE \"{$this->_schema}\".\"{$table}\"";
+ }
+ else
+ $sql = "ANALYZE";
return $this->execute($sql);
}
- // Language functions
-
/**
- * Gets all languages
- * @param $all True to get all languages, regardless of show_system
- * @return A recordset
+ * Vacuums a database
+ * @param $table The table to vacuum
+ * @param $analyze If true, also does analyze
+ * @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
+ * @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
*/
- function getLanguages($all = false) {
- global $conf;
-
- if ($conf['show_system'] || $all)
- $where = '';
- else
- $where = 'WHERE lanispl';
+ function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) {
- $sql = "
- SELECT
- lanname,
- lanpltrusted,
- lanplcallfoid::regproc AS lanplcallf
- FROM
- pg_language
- {$where}
- ORDER BY
- lanname
- ";
+ $sql = "VACUUM";
+ if ($full) $sql .= " FULL";
+ if ($freeze) $sql .= " FREEZE";
+ if ($analyze) $sql .= " ANALYZE";
+ if ($table != '') {
+ $this->fieldClean($table);
+ $sql .= " \"{$this->_schema}\".\"{$table}\"";
+ }
- return $this->selectSet($sql);
+ return $this->execute($sql);
}
- // Aggregate functions
-
/**
- * Gets all aggregates
+ * Returns all available process information.
* @return A recordset
*/
- function getAggregates() {
- global $conf;
-
- if ($conf['show_system'])
- $where = '';
- else
- $where = "WHERE a.oid > '{$this->_lastSystemOID}'::oid";
-
+ function getAutovacuum() {
$sql = "
- SELECT
- a.aggname AS proname,
- CASE a.aggbasetype
- WHEN 0 THEN NULL
- ELSE (SELECT typname FROM pg_type t WHERE t.oid=a.aggbasetype)
- END AS proargtypes,
- (SELECT description FROM pg_description pd WHERE a.oid=pd.objoid) AS aggcomment
- FROM
- pg_aggregate a
- {$where}
- ORDER BY
- 1, 2;
- ";
+ SELECT vacrelid, nspname, relname, enabled, vac_base_thresh,
+ vac_scale_factor, anl_base_thresh, anl_scale_factor, vac_cost_delay, vac_cost_limit
+ FROM pg_autovacuum
+ join pg_class on (oid=vacrelid)
+ join pg_namespace on (oid=relnamespace)
+ ORDER BY nspname, relname";
return $this->selectSet($sql);
}
/**
- * Gets all information for an aggregate
- * @param $name The name of the aggregate
- * @param $basetype The input data type of the aggregate
+ * Returns all available process information.
+ * @param $database (optional) Find only connections to specified database
* @return A recordset
*/
- function getAggregate($name, $basetype) {
- $this->fieldclean($name);
- $this->fieldclean($basetype);
-
+ function getProcesses($database = null) {
+ if ($database === null)
+ $sql = "SELECT * FROM pg_catalog.pg_stat_activity ORDER BY datname, usename, procpid";
+ else {
+ $this->clean($database);
$sql = "
- SELECT a.aggname AS proname,
- CASE a.aggbasetype
- WHEN 0 THEN NULL
- ELSE format_type(a.aggbasetype, NULL)
- END AS proargtypes,
- a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn, a.agginitval, u.usename,
- obj_description(a.oid, 'pg_aggregate') AS aggrcomment
- FROM pg_user u, pg_aggregate a
- WHERE a.aggowner=u.usesysid
- AND a.aggname='" . $name . "'
- AND CASE a.aggbasetype
- WHEN 0 THEN ''
- ELSE format_type(a.aggbasetype, NULL)
- END ='" . $basetype . "'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a new aggregate in the database
- * @param $name The name of the aggregate
- * @param $basetype The input data type of the aggregate
- * @param $sfunc The name of the state transition function for the aggregate
- * @param $stype The data type for the aggregate's state value
- * @param $ffunc The name of the final function for the aggregate
- * @param $initcond The initial setting for the state value
- * @param $sortop The sort operator for the aggregate
- * @param $comment Aggregate comment
- * @return 0 success
- * @return -1 error
- */
- function createAggregate($name, $basetype, $sfunc, $stype, $ffunc, $initcond, $sortop, $comment) {
- $this->fieldClean($name);
- $this->fieldClean($basetype);
- $this->fieldClean($sfunc);
- $this->fieldClean($stype);
- $this->fieldClean($ffunc);
- $this->fieldClean($initcond);
- $this->fieldClean($sortop);
- $this->clean($comment);
-
- $this->beginTransaction();
-
- $schema = $this->schema();
- $sql = "CREATE AGGREGATE {$schema}\"{$name}\" (BASETYPE = \"{$basetype}\", SFUNC = \"{$sfunc}\", STYPE = \"{$stype}\"";
- if(trim($ffunc) != '') $sql .= ", FINALFUNC = \"{$ffunc}\"";
- if(trim($initcond) != '') $sql .= ", INITCOND = \"{$initcond}\"";
- if(trim($sortop) != '') $sql .= ", SORTOP = \"{$sortop}\"";
- $sql .= ")";
-
- $status = $this->execute($sql);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
-
- if (trim($comment) != '') {
- $status = $this->setComment('AGGREGATE', $name, '', $comment, $basetype);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
+ SELECT * FROM pg_catalog.pg_stat_activity
+ WHERE datname='{$database}' ORDER BY usename, procpid";
}
- return $this->endTransaction();
- }
-
- /**
- * Removes an aggregate function from the database
- * @param $aggrname The name of the aggregate
- * @param $aggrtype The input data type of the aggregate
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropAggregate($aggrname, $aggrtype, $cascade) {
- $this->fieldClean($aggrname);
- $this->fieldClean($aggrtype);
-
- $sql = "DROP AGGREGATE \"{$aggrname}\" (\"{$aggrtype}\")";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
- // Operator Class functions
-
/**
- * Gets all opclasses
+ * Returns table locks information in the current database
* @return A recordset
*/
- function getOpClasses() {
+
+ function getLocks() {
global $conf;
- if ($conf['show_system'])
- $where = '';
+ if (!$conf['show_system'])
+ $where = "AND pn.nspname NOT LIKE 'pg\\\\_%'";
else
- $where = "AND po.oid > '{$this->_lastSystemOID}'::oid";
+ $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
$sql = "
- SELECT DISTINCT
- pa.amname,
- po.opcname,
- (SELECT typname FROM pg_type t WHERE t.oid=opcdeftype) AS opcintype,
- TRUE AS opcdefault,
- NULL::text AS opccomment
+ SELECT
+ pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction,
+ (select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid'
+ and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction
FROM
- pg_opclass po, pg_am pa, pg_amop pam
+ pg_catalog.pg_locks pl,
+ pg_catalog.pg_class pc,
+ pg_catalog.pg_namespace pn
WHERE
- pam.amopid=pa.oid
- AND pam.amopclaid=po.oid
- {$where}
- ORDER BY 1,2
- ";
+ pl.relation = pc.oid AND pc.relnamespace=pn.oid
+ {$where}
+ ORDER BY pid,nspname,tablename";
return $this->selectSet($sql);
}
- // Type conversion routines
-
/**
- * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false
- * @param $parameter the parameter
+ * Sends a cancel or kill command to a process
+ * @param $pid The ID of the backend process
+ * @param $signal 'CANCEL'
+ * @return 0 success
+ * @return -1 invalid signal type
*/
- function dbBool(&$parameter) {
- if ($parameter) $parameter = 't';
- else $parameter = 'f';
+ function sendSignal($pid, $signal) {
+ // Clean
+ $pid = (int)$pid;
- return $parameter;
- }
+ if ($signal == 'CANCEL')
+ $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
+ else
+ return -1;
- /**
- * Change a parameter from 't' or 'f' to a boolean, (others evaluate to false)
- * @param $parameter the parameter
- */
- function phpBool($parameter) {
- $parameter = ($parameter == 't');
- return $parameter;
+ // Execute the query
+ $val = $this->selectField($sql, 'val');
+
+ if ($val === -1) return -1;
+ elseif ($val == '1') return 0;
+ else return -1;
}
// Misc functions
@@ -4145,7 +6743,7 @@ class Postgres extends ADODB_base {
break;
default:
// Unknown object type
- return -1;
+ return -1;
}
if ($comment != '')
@@ -4158,446 +6756,16 @@ class Postgres extends ADODB_base {
}
/**
- * Returns the SQL for changing the current user
- * @param $user The user to change to
- * @return The SQL
- */
- function getChangeUserSQL($user) {
- $this->fieldClean($user);
- return "\\connect - \"{$user}\"";
- }
-
- /**
- * Sets up the data object for a dump. eg. Starts the appropriate
- * transaction, sets variables, etc.
- * @return 0 success
- */
- function beginDump() {
- // Begin serializable transaction (to dump consistent data)
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- // Set serializable
- $sql = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Set datestyle to ISO
- $sql = "SET DATESTYLE = ISO";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- /**
- * Ends the data object for a dump.
+ * Sets the client encoding
+ * @param $encoding The encoding to for the client
* @return 0 success
*/
- function endDump() {
- return $this->endTransaction();
- }
-
- /**
- * Generates the SQL for the 'select' function
- * @param $table The table from which to select
- * @param $show An array of columns to show. Empty array means all columns.
- * @param $values An array mapping columns to values
- * @param $ops An array of the operators to use
- * @param $orderby (optional) An array of column numbers or names (one based)
- * mapped to sort direction (asc or desc or '' or null) to order by
- * @return The SQL query
- */
- function getSelectSQL($table, $show, $values, $ops, $orderby = array()) {
- $this->fieldClean($table);
- $this->fieldArrayClean($show);
-
- // If an empty array is passed in, then show all columns
- if (sizeof($show) == 0) {
- if ($this->hasObjectID($table))
- $sql = "SELECT \"{$this->id}\", * FROM ";
- else
- $sql = "SELECT * FROM ";
- }
- else {
- // Add oid column automatically to results for editing purposes
- if (!in_array($this->id, $show) && $this->hasObjectID($table))
- $sql = "SELECT \"{$this->id}\", \"";
- else
- $sql = "SELECT \"";
-
- $sql .= join('","', $show) . "\" FROM ";
- }
-
- if ($this->hasSchemas() && isset($_REQUEST['schema'])) {
- $this->fieldClean($_REQUEST['schema']);
- $sql .= "\"{$_REQUEST['schema']}\".";
- }
- $sql .= "\"{$table}\"";
-
- // If we have values specified, add them to the WHERE clause
- $first = true;
- if (is_array($values) && sizeof($values) > 0) {
- foreach ($values as $k => $v) {
- if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
- $this->fieldClean($k);
- if ($first) {
- $sql .= " WHERE ";
- $first = false;
- } else {
- $sql .= " AND ";
- }
- // Different query format depending on operator type
- switch ($this->selectOps[$ops[$k]]) {
- case 'i':
- // Only clean the field for the inline case
- // this is because (x), subqueries need to
- // to allow 'a','b' as input.
- $this->clean($v);
- $sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
- break;
- case 'p':
- $sql .= "\"{$k}\" {$ops[$k]}";
- break;
- case 'x':
- $sql .= "\"{$k}\" {$ops[$k]} ({$v})";
- break;
- case 't':
- $sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
- break;
- default:
- // Shouldn't happen
- }
- }
- }
- }
-
- // ORDER BY
- if (is_array($orderby) && sizeof($orderby) > 0) {
- $sql .= " ORDER BY ";
- $first = true;
- foreach ($orderby as $k => $v) {
- if ($first) $first = false;
- else $sql .= ', ';
- if (ereg('^[0-9]+$', $k)) {
- $sql .= $k;
- }
- else {
- $this->fieldClean($k);
- $sql .= '"' . $k . '"';
- }
- if (strtoupper($v) == 'DESC') $sql .= " DESC";
- }
- }
-
- return $sql;
- }
-
- /**
- * Finds the number of rows that would be returned by a
- * query.
- * @param $query The SQL query
- * @param $count The count query
- * @return The count of rows
- * @return -1 error
- */
- function browseQueryCount($query, $count) {
- // Count the number of rows
- $rs = $this->selectSet($query);
- if (!is_object($rs)) {
- return -1;
- }
-
- return $rs->recordCount();
- }
-
- /**
- * Returns a recordset of all columns in a query. Supports paging.
- * @param $type Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
- * or 'SELECT" if it's a select query
- * @param $table The base table of the query. NULL for no table.
- * @param $query The query that is being executed. NULL for no query.
- * @param $sortkey The column number to sort by, or '' or null for no sorting
- * @param $sortdir The direction in which to sort the specified column ('asc' or 'desc')
- * @param $page The page of the relation to retrieve
- * @param $page_size The number of rows per page
- * @param &$max_pages (return-by-ref) The max number of pages in the relation
- * @return A recordset on success
- * @return -1 transaction error
- * @return -2 counting error
- * @return -3 page or page_size invalid
- * @return -4 unknown type
- * @return -5 failed setting transaction read only
- */
- function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages) {
- // Check that we're not going to divide by zero
- if (!is_numeric($page_size) || $page_size != (int)$page_size || $page_size <= 0) return -3;
-
- // If $type is TABLE, then generate the query
- switch ($type) {
- case 'TABLE':
- if (ereg('^[0-9]+$', $sortkey) && $sortkey > 0) $orderby = array($sortkey => $sortdir);
- else $orderby = array();
- $query = $this->getSelectSQL($table, array(), array(), array(), $orderby);
- break;
- case 'QUERY':
- case 'SELECT':
- // Trim query
- $query = trim($query);
- // Trim off trailing semi-colon if there is one
- if (substr($query, strlen($query) - 1, 1) == ';')
- $query = substr($query, 0, strlen($query) - 1);
- break;
- default:
- return -4;
- }
-
- // Generate count query
- $count = "SELECT COUNT(*) AS total FROM ($query) AS sub";
-
- // Open a transaction
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- // If backend supports read only queries, then specify read only mode
- // to avoid side effects from repeating queries that do writes.
- if ($this->hasReadOnlyQueries()) {
- $status = $this->execute("SET TRANSACTION READ ONLY");
- if ($status != 0) {
- $this->rollbackTransaction();
- return -5;
- }
- }
-
-
- // Count the number of rows
- $total = $this->browseQueryCount($query, $count);
- if ($total < 0) {
- $this->rollbackTransaction();
- return -2;
- }
-
- // Calculate max pages
- $max_pages = ceil($total / $page_size);
-
- // Check that page is less than or equal to max pages
- if (!is_numeric($page) || $page != (int)$page || $page > $max_pages || $page < 1) {
- $this->rollbackTransaction();
- return -3;
- }
-
- // Set fetch mode to NUM so that duplicate field names are properly returned
- // for non-table queries. Since the SELECT feature only allows selecting one
- // table, duplicate fields shouldn't appear.
- if ($type == 'QUERY') $this->conn->setFetchMode(ADODB_FETCH_NUM);
-
- // Figure out ORDER BY. Sort key is always the column number (based from one)
- // of the column to order by. Only need to do this for non-TABLE queries
- if ($type != 'TABLE' && ereg('^[0-9]+$', $sortkey) && $sortkey > 0) {
- $orderby = " ORDER BY {$sortkey}";
- // Add sort order
- if ($sortdir == 'desc')
- $orderby .= ' DESC';
- else
- $orderby .= ' ASC';
- }
- else $orderby = '';
-
- // Actually retrieve the rows, with offset and limit
- if ($this->hasFullSubqueries())
- $rs = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
- else
- $rs = $this->selectSet("{$query} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
- $status = $this->endTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- return $rs;
- }
-
- /**
- * Returns a recordset of all columns in a relation. Used for data export.
- * @@ Note: Really needs to use a cursor
- * @param $relation The name of a relation
- * @return A recordset on success
- * @return -1 Failed to set datestyle
- */
- function dumpRelation($relation, $oids) {
- $this->fieldClean($relation);
-
- // Actually retrieve the rows
- if ($oids) $oid_str = $this->id . ', ';
- else $oid_str = '';
-
- return $this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
- }
-
- /**
- * Searches all system catalogs to find objects that match a certain name.
- * @param $term The search term
- * @param $filter The object type to restrict to ('' means no restriction)
- * @return A recordset
- */
- function findObject($term, $filter) {
- global $conf;
-
- // Escape search term for ~* match
- $special = array('.', '*', '^', '$', ':', '?', '+', ',', '=', '!', '[', ']', '(', ')', '{', '}', '<', '>', '-', '\\');
- foreach ($special as $v) {
- $term = str_replace($v, "\\{$v}", $term);
- }
- $this->clean($term);
- $this->clean($filter);
-
- // Build SQL, excluding system relations as necessary
- // Relations
- $case_clause = "CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'VIEW'::VARCHAR ELSE 'TABLE'::VARCHAR END) WHEN relkind='v' THEN 'VIEW'::VARCHAR WHEN relkind='S' THEN 'SEQUENCE'::VARCHAR END";
- $sql = "
- SELECT {$case_clause} AS type,
- pc.oid, NULL::VARCHAR AS schemaname, NULL::VARCHAR AS relname, pc.relname AS name FROM pg_class pc
- WHERE relkind IN ('r', 'v', 'S') AND relname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter == 'TABLE' || $filter == 'VIEW' || $filter == 'SEQUENCE') $sql .= " AND {$case_clause} = '{$filter}'";
- elseif ($filter != '') $sql .= " AND FALSE";
-
- // Columns
- $sql .= "
- UNION ALL
- SELECT CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'COLUMNVIEW'::VARCHAR ELSE 'COLUMNTABLE'::VARCHAR END) WHEN relkind='v' THEN 'COLUMNVIEW'::VARCHAR END,
- NULL, NULL, pc.relname, pa.attname FROM pg_class pc,
- pg_attribute pa WHERE pc.oid=pa.attrelid
- AND pa.attname ~* '.*{$term}.*' AND pa.attnum > 0 AND pc.relkind IN ('r', 'v')";
- if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'COLUMNTABLE' || $filter != 'COLUMNVIEW') $sql .= " AND FALSE";
-
- // Functions
- $sql .= "
- UNION ALL
- SELECT 'FUNCTION', pp.oid, NULL, NULL, pp.proname || '(' || oidvectortypes(pp.proargtypes) || ')' FROM pg_proc pp
- WHERE proname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND pp.oid > '{$this->_lastSystemOID}'::oid";
- if ($filter != '' && $filter != 'FUNCTION') $sql .= " AND FALSE";
-
- // Indexes
- $sql .= "
- UNION ALL
- SELECT 'INDEX', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
- pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
- AND pi.indexrelid=pc2.oid
- AND pc2.relname ~* '.*{$term}.*' AND NOT pi.indisprimary AND NOT pi.indisunique";
- if (!$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'INDEX') $sql .= " AND FALSE";
-
- // Check Constraints
- $sql .= "
- UNION ALL
- SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pr.rcname FROM pg_class pc,
- pg_relcheck pr WHERE pc.oid=pr.rcrelid
- AND pr.rcname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'CONSTRAINT') $sql .= " AND FALSE";
-
- // Unique and Primary Key Constraints
- $sql .= "
- UNION ALL
- SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
- pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
- AND pi.indexrelid=pc2.oid
- AND pc2.relname ~* '.*{$term}.*' AND (pi.indisprimary OR pi.indisunique)";
- if (!$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'CONSTRAINT') $sql .= " AND FALSE";
-
- // Triggers
- $sql .= "
- UNION ALL
- SELECT 'TRIGGER', NULL, NULL, pc.relname, pt.tgname FROM pg_class pc,
- pg_trigger pt WHERE pc.oid=pt.tgrelid
- AND pt.tgname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'TRIGGER') $sql .= " AND FALSE";
-
- // Table Rules
- $sql .= "
- UNION ALL
- SELECT 'RULETABLE', NULL, NULL, c.relname AS tablename, r.rulename
- FROM pg_rewrite r, pg_class c
- WHERE c.relkind='r' AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
- AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'RULE') $sql .= " AND FALSE";
-
- // View Rules
- $sql .= "
- UNION ALL
- SELECT 'RULEVIEW', NULL, NULL, c.relname AS tablename, r.rulename
- FROM pg_rewrite r, pg_class c
- WHERE c.relkind='r' AND EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
- AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg@_%' ESCAPE '@'";
- if ($filter != '' && $filter != 'RULE') $sql .= " AND FALSE";
-
- // Advanced Objects
- if ($conf['show_advanced']) {
- // Types
- $sql .= "
- UNION ALL
- SELECT 'TYPE', pt.oid, NULL, NULL, pt.typname FROM pg_type pt
- WHERE typname ~* '.*{$term}.*' AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_class c WHERE c.oid = pt.typrelid))";
- if (!$conf['show_system']) $sql .= " AND pt.oid > '{$this->_lastSystemOID}'::oid";
- if ($filter != '' && $filter != 'TYPE') $sql .= " AND FALSE";
-
- // Operators
- $sql .= "
- UNION ALL
- SELECT 'OPERATOR', po.oid, NULL, NULL, po.oprname FROM pg_operator po
- WHERE oprname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
- if ($filter != '' && $filter != 'OPERATOR') $sql .= " AND FALSE";
-
- // Languages
- $sql .= "
- UNION ALL
- SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_language pl
- WHERE lanname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND pl.lanispl";
- if ($filter != '' && $filter != 'LANGUAGE') $sql .= " AND FALSE";
-
- // Aggregates
- $sql .= "
- UNION ALL
- SELECT DISTINCT ON (a.aggname) 'AGGREGATE', a.oid, NULL, NULL, a.aggname FROM pg_aggregate a
- WHERE aggname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND a.oid > '{$this->_lastSystemOID}'::oid";
- if ($filter != '' && $filter != 'AGGREGATE') $sql .= " AND FALSE";
-
- // Op Classes
- $sql .= "
- UNION ALL
- SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, NULL, NULL, po.opcname FROM pg_opclass po
- WHERE po.opcname ~* '.*{$term}.*'";
- if (!$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
- if ($filter != '' && $filter != 'OPCLASS') $sql .= " AND FALSE";
- }
-
- $sql .= " ORDER BY type, schemaname, relname, name";
+ function setClientEncoding($encoding) {
+ $this->clean($encoding);
- return $this->selectSet($sql);
- }
+ $sql = "SET CLIENT_ENCODING TO '{$encoding}'";
- /**
- * Private helper method to detect a valid $foo$ quote delimiter at
- * the start of the parameter dquote
- * @return True if valid, false otherwise
- */
- function valid_dolquote($dquote) {
- // XXX: support multibyte
- return (ereg('^[$][$]', $dquote) || ereg('^[$][_[:alpha:]][_[:alnum:]]*[$]', $dquote));
+ return $this->execute($sql);
}
/**
@@ -4609,6 +6777,7 @@ class Postgres extends ADODB_base {
* @param &$prevlen Length of previous character (ie. 1)
* @param &$thislen Length of current character (ie. 1)
*/
+ private
function advance_1(&$i, &$prevlen, &$thislen) {
$prevlen = $thislen;
$i += $thislen;
@@ -4616,6 +6785,17 @@ class Postgres extends ADODB_base {
}
/**
+ * Private helper method to detect a valid $foo$ quote delimiter at
+ * the start of the parameter dquote
+ * @return True if valid, false otherwise
+ */
+ private
+ function valid_dolquote($dquote) {
+ // XXX: support multibyte
+ return (ereg('^[$][$]', $dquote) || ereg('^[$][_[:alpha:]][_[:alnum:]]*[$]', $dquote));
+ }
+
+ /**
* Executes an SQL script as a series of SQL statements. Returns
* the result of the final step. This is a very complicated lexer
* based on the REL7_4_STABLE src/bin/psql/mainloop.c lexer in
@@ -4728,7 +6908,7 @@ class Postgres extends ADODB_base {
/*
* start of $foo$ type quote?
- */
+ */
else if (!$dol_quote && $this->valid_dolquote(substr($line, $i))) {
$dol_end = strpos(substr($line, $i + 1), '$');
$dol_quote = substr($line, $i, $dol_end + 1);
@@ -4749,7 +6929,7 @@ class Postgres extends ADODB_base {
/* count nested parentheses */
else if (substr($line, $i, 1) == '(') {
$paren_level++;
- }
+ }
else if (substr($line, $i, 1) == ')' && $paren_level > 0) {
$paren_level--;
@@ -4765,7 +6945,7 @@ class Postgres extends ADODB_base {
/*
* insert a cosmetic newline, if this is not the first
* line in the buffer
- */
+ */
if (strlen($query_buf) > 0)
$query_buf .= "\n";
/* append the line to the query buffer */
@@ -4791,8 +6971,8 @@ class Postgres extends ADODB_base {
break;
}
}
- }
- }
+ }
+ }
$query_buf = null;
$query_start = $i + $thislen;
@@ -4803,7 +6983,7 @@ class Postgres extends ADODB_base {
* We grab the whole string so that we don't
* mistakenly see $foo$ inside an identifier as the start
* of a dollar quote.
- */
+ */
// XXX: multibyte here
else if (ereg('^[_[:alpha:]]$', substr($line, $i, 1))) {
$sub = substr($line, $i, $thislen);
@@ -4815,7 +6995,7 @@ class Postgres extends ADODB_base {
// Since we're now over the next character to be examined, it is necessary
// to move back one space.
$i-=$prevlen;
- }
+ }
} // end for
/* Put the rest of the line in the query buffer. */
@@ -4834,7 +7014,7 @@ class Postgres extends ADODB_base {
/*
* Process query at the end of file without a semicolon, so long as
* it's non-empty.
- */
+ */
if (strlen($query_buf) > 0 && strspn($query_buf, " \t\n\r") != strlen($query_buf))
{
// Execute the query (supporting 4.1.x PHP...)
@@ -4853,77 +7033,412 @@ class Postgres extends ADODB_base {
if ($copy == "\\.\n" || $copy == "\\.\r\n") {
pg_end_copy($conn);
break;
+ }
+ }
+ }
+ }
+
+ fclose($fd);
+
+ return true;
+ }
+
+ /**
+ * Generates the SQL for the 'select' function
+ * @param $table The table from which to select
+ * @param $show An array of columns to show. Empty array means all columns.
+ * @param $values An array mapping columns to values
+ * @param $ops An array of the operators to use
+ * @param $orderby (optional) An array of column numbers or names (one based)
+ * mapped to sort direction (asc or desc or '' or null) to order by
+ * @return The SQL query
+ */
+ function getSelectSQL($table, $show, $values, $ops, $orderby = array()) {
+ $this->fieldClean($table);
+ $this->fieldArrayClean($show);
+
+ // If an empty array is passed in, then show all columns
+ if (sizeof($show) == 0) {
+ if ($this->hasObjectID($table))
+ $sql = "SELECT \"{$this->id}\", * FROM ";
+ else
+ $sql = "SELECT * FROM ";
+ }
+ else {
+ // Add oid column automatically to results for editing purposes
+ if (!in_array($this->id, $show) && $this->hasObjectID($table))
+ $sql = "SELECT \"{$this->id}\", \"";
+ else
+ $sql = "SELECT \"";
+
+ $sql .= join('","', $show) . "\" FROM ";
+ }
+
+ if ($this->hasSchemas() && isset($_REQUEST['schema'])) {
+ $this->fieldClean($_REQUEST['schema']);
+ $sql .= "\"{$_REQUEST['schema']}\".";
+ }
+ $sql .= "\"{$table}\"";
+
+ // If we have values specified, add them to the WHERE clause
+ $first = true;
+ if (is_array($values) && sizeof($values) > 0) {
+ foreach ($values as $k => $v) {
+ if ($v != '' || $this->selectOps[$ops[$k]] == 'p') {
+ $this->fieldClean($k);
+ if ($first) {
+ $sql .= " WHERE ";
+ $first = false;
+ } else {
+ $sql .= " AND ";
+ }
+ // Different query format depending on operator type
+ switch ($this->selectOps[$ops[$k]]) {
+ case 'i':
+ // Only clean the field for the inline case
+ // this is because (x), subqueries need to
+ // to allow 'a','b' as input.
+ $this->clean($v);
+ $sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
+ break;
+ case 'p':
+ $sql .= "\"{$k}\" {$ops[$k]}";
+ break;
+ case 'x':
+ $sql .= "\"{$k}\" {$ops[$k]} ({$v})";
+ break;
+ case 't':
+ $sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
+ break;
+ default:
+ // Shouldn't happen
}
}
}
+ }
+
+ // ORDER BY
+ if (is_array($orderby) && sizeof($orderby) > 0) {
+ $sql .= " ORDER BY ";
+ $first = true;
+ foreach ($orderby as $k => $v) {
+ if ($first) $first = false;
+ else $sql .= ', ';
+ if (ereg('^[0-9]+$', $k)) {
+ $sql .= $k;
+ }
+ else {
+ $this->fieldClean($k);
+ $sql .= '"' . $k . '"';
+ }
+ if (strtoupper($v) == 'DESC') $sql .= " DESC";
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Returns a recordset of all columns in a query. Supports paging.
+ * @param $type Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
+ * or 'SELECT" if it's a select query
+ * @param $table The base table of the query. NULL for no table.
+ * @param $query The query that is being executed. NULL for no query.
+ * @param $sortkey The column number to sort by, or '' or null for no sorting
+ * @param $sortdir The direction in which to sort the specified column ('asc' or 'desc')
+ * @param $page The page of the relation to retrieve
+ * @param $page_size The number of rows per page
+ * @param &$max_pages (return-by-ref) The max number of pages in the relation
+ * @return A recordset on success
+ * @return -1 transaction error
+ * @return -2 counting error
+ * @return -3 page or page_size invalid
+ * @return -4 unknown type
+ * @return -5 failed setting transaction read only
+ */
+ function browseQuery($type, $table, $query, $sortkey, $sortdir, $page, $page_size, &$max_pages) {
+ // Check that we're not going to divide by zero
+ if (!is_numeric($page_size) || $page_size != (int)$page_size || $page_size <= 0) return -3;
+
+ // If $type is TABLE, then generate the query
+ switch ($type) {
+ case 'TABLE':
+ if (ereg('^[0-9]+$', $sortkey) && $sortkey > 0) $orderby = array($sortkey => $sortdir);
+ else $orderby = array();
+ $query = $this->getSelectSQL($table, array(), array(), array(), $orderby);
+ break;
+ case 'QUERY':
+ case 'SELECT':
+ // Trim query
+ $query = trim($query);
+ // Trim off trailing semi-colon if there is one
+ if (substr($query, strlen($query) - 1, 1) == ';')
+ $query = substr($query, 0, strlen($query) - 1);
+ break;
+ default:
+ return -4;
+ }
+
+ // Generate count query
+ $count = "SELECT COUNT(*) AS total FROM ($query) AS sub";
+
+ // Open a transaction
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ // If backend supports read only queries, then specify read only mode
+ // to avoid side effects from repeating queries that do writes.
+ if ($this->hasReadOnlyQueries()) {
+ $status = $this->execute("SET TRANSACTION READ ONLY");
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -5;
+ }
+ }
+
+
+ // Count the number of rows
+ $total = $this->browseQueryCount($query, $count);
+ if ($total < 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+
+ // Calculate max pages
+ $max_pages = ceil($total / $page_size);
+
+ // Check that page is less than or equal to max pages
+ if (!is_numeric($page) || $page != (int)$page || $page > $max_pages || $page < 1) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+
+ // Set fetch mode to NUM so that duplicate field names are properly returned
+ // for non-table queries. Since the SELECT feature only allows selecting one
+ // table, duplicate fields shouldn't appear.
+ if ($type == 'QUERY') $this->conn->setFetchMode(ADODB_FETCH_NUM);
+
+ // Figure out ORDER BY. Sort key is always the column number (based from one)
+ // of the column to order by. Only need to do this for non-TABLE queries
+ if ($type != 'TABLE' && ereg('^[0-9]+$', $sortkey) && $sortkey > 0) {
+ $orderby = " ORDER BY {$sortkey}";
+ // Add sort order
+ if ($sortdir == 'desc')
+ $orderby .= ' DESC';
+ else
+ $orderby .= ' ASC';
+ }
+ else $orderby = '';
+
+ // Actually retrieve the rows, with offset and limit
+ if ($this->hasFullSubqueries())
+ $rs = $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
+ else
+ $rs = $this->selectSet("{$query} LIMIT {$page_size} OFFSET " . ($page - 1) * $page_size);
+ $status = $this->endTransaction();
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ return $rs;
+ }
+
+ /**
+ * Finds the number of rows that would be returned by a
+ * query.
+ * @param $query The SQL query
+ * @param $count The count query
+ * @return The count of rows
+ * @return -1 error
+ */
+ function browseQueryCount($query, $count) {
+ return $this->selectField($count, 'total');
+ }
+
+ /**
+ * Returns a recordset of all columns in a table
+ * @param $table The name of a table
+ * @param $key The associative array holding the key to retrieve
+ * @return A recordset
+ */
+ function browseRow($table, $key) {
+ $this->fieldClean($table);
+
+ $schema = $this->schema();
+
+ $sql = "SELECT * FROM {$schema}\"{$table}\"";
+ if (is_array($key) && sizeof($key) > 0) {
+ $sql .= " WHERE true";
+ foreach ($key as $k => $v) {
+ $this->fieldClean($k);
+ $this->clean($v);
+ $sql .= " AND \"{$k}\"='{$v}'";
+ }
+ }
+
+ return $this->selectSet($sql);
+ }
+
+ // Type conversion routines
+
+ /**
+ * Change the value of a parameter to 't' or 'f' depending on whether it evaluates to true or false
+ * @param $parameter the parameter
+ */
+ function dbBool(&$parameter) {
+ if ($parameter) $parameter = 't';
+ else $parameter = 'f';
+
+ return $parameter;
+ }
+
+ /**
+ * Change a parameter from 't' or 'f' to a boolean, (others evaluate to false)
+ * @param $parameter the parameter
+ */
+ function phpBool($parameter) {
+ $parameter = ($parameter == 't');
+ return $parameter;
+ }
+
+ // interfaces Statistics collector functions
+
+ /**
+ * Fetches statistics for a database
+ * @param $database The database to fetch stats for
+ * @return A recordset
+ */
+ function getStatsDatabase($database) {
+ $this->clean($database);
+
+ $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Fetches tuple statistics for a table
+ * @param $table The table to fetch stats for
+ * @return A recordset
+ */
+ function getStatsTableTuples($table) {
+ $this->clean($table);
+
+ $sql = 'SELECT * FROM pg_stat_all_tables WHERE';
+ if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
+ $sql .= " relname='{$table}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Fetches I/0 statistics for a table
+ * @param $table The table to fetch stats for
+ * @return A recordset
+ */
+ function getStatsTableIO($table) {
+ $this->clean($table);
+
+ $sql = 'SELECT * FROM pg_statio_all_tables WHERE';
+ if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
+ $sql .= " relname='{$table}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Fetches tuple statistics for all indexes on a table
+ * @param $table The table to fetch index stats for
+ * @return A recordset
+ */
+ function getStatsIndexTuples($table) {
+ $this->clean($table);
+
+ $sql = 'SELECT * FROM pg_stat_all_indexes WHERE';
+ if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
+ $sql .= " relname='{$table}' ORDER BY indexrelname";
+
+ return $this->selectSet($sql);
}
- fclose($fd);
+ /**
+ * Fetches I/0 statistics for all indexes on a table
+ * @param $table The table to fetch index stats for
+ * @return A recordset
+ */
+ function getStatsIndexIO($table) {
+ $this->clean($table);
- return true;
+ $sql = 'SELECT * FROM pg_statio_all_indexes WHERE';
+ if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
+ $sql .= " relname='{$table}' ORDER BY indexrelname";
+
+ return $this->selectSet($sql);
}
// Capabilities
- function hasAlterDatabaseOwner() { return false; }
- function hasAlterDatabaseRename() { return false; }
+
+ function hasAggregateSortOp() { return true; }
+ function hasAlterAggregate() { return true; }
+ function hasAlterColumnType() { return true; }
+ function hasAlterDatabaseOwner() { return true; }
+ function hasAlterDatabaseRename() { return true; }
+ function hasAlterSequenceOwner() { return true; }
+ function hasAlterSequenceProps() { return true; }
+ function hasAlterTableOwner() { return true; }
+ function hasAlterTableSchema() { return true; }
+ function hasAlterTrigger() { return true; }
+ function hasAnalyze() { return true; }
+ function hasAutovacuum() { return true; }
+ function hasCasts() { return true; }
+ function hasCompositeTypes() { return true; }
+ function hasConstraintsInfo() { return true; }
+ function hasConversions() { return true; }
+ function hasCreateTableLike() { return true; }
+ function hasCreateTableLikeWithConstraints() { return true; }
+ function hasCreateTableLikeWithIndexes() { return true; }
+ function hasDisableTriggers() { return true; }
+ function hasDomainConstraints() { return true; }
+ function hasDomains() { return true; }
+ function hasDropBehavior() { return true; }
+ function hasDropColumn() { return true; }
+ function hasEnumTypes() { return true; }
+ function hasFTS() { return true; }
+ function hasFullSubqueries() { return true; }
+ function hasFullVacuum() { return true; }
+ function hasFuncPrivs() { return true; }
+ function hasFunctionAlterOwner() { return true; }
+ function hasFunctionAlterSchema() { return true; }
+ function hasFunctionCosting() { return true; }
+ function hasFunctionGUC() { return true; }
+ function hasGrantOption() { return true; }
+ function hasIsClustered() { return true; }
+ function hasLocksView() { return true; }
+ function hasNamedParams() { return true; }
+ function hasObjectID() { return true; }
+ function hasPartialIndexes() { return true; }
+ function hasPrepare() { return true; }
+ function hasPreparedXacts() { return true; }
+ function hasProcesses() { return true; }
+ function hasReadOnlyQueries() { return true; }
+ function hasRecluster() { return true; }
+ function hasRoles() { return true; }
+ function hasSchemas() { return true; }
+ function hasSequenceAlterSchema() { return true; }
+ function hasServerAdminFuncs() { return true; }
+ function hasSharedComments() { return true; }
+ function hasSignals() { return true; }
+ function hasStatsCollector() { return true; }
+ function hasTablespaces() { return true; }
+ function hasUserAndDbVariables() { return true; }
+ function hasUserRename() { return true; }
+ function hasUserSessionDefaults() { return true; }
+ function hasVariables() { return true; }
+ function hasViewColumnRename() { return true; }
+ function hasVirtualTransactionId() { return true; }
+ function hasWithoutOIDs() { return true; }
function hasAlterDatabase() { return $this->hasAlterDatabaseRename(); }
- function hasSchemas() { return false; }
- function hasConversions() { return false; }
- function hasGrantOption() { return false; }
- function hasIsClustered() { return false; }
- function hasDropBehavior() { return false; }
- function hasDropColumn() { return false; }
- function hasDomains() { return false; }
- function hasDomainConstraints() { return false; }
- function hasAlterTrigger() { return false; }
- function hasWithoutOIDs() { return false; }
- function hasAlterTableOwner() { return false; }
- function hasAlterTableSchema() { return false; }
- function hasAlterSequenceOwner() { return false; }
- function hasAlterSequenceProps() { return false; }
- function hasSequenceAlterSchema() { return false; }
- function hasPartialIndexes() { return false; }
- function hasCasts() { return false; }
- function hasFullSubqueries() { return false; }
- function hasPrepare() { return false; }
- function hasProcesses() { return false; }
- function hasVariables() { return false; }
- function hasStatsCollector() { return false; }
- function hasAlterColumnType() { return false; }
- function hasUserSessionDefaults() { return false; }
- function hasUserRename() { return false; }
- function hasRecluster() { return false; }
- function hasFullVacuum() { return false; }
- function hasConstraintsInfo() { return false; }
function hasForeignKeysInfo() { return $this->hasConstraintsInfo(); }
- function hasViewColumnRename() { return false; }
- function hasTablespaces() { return false; }
- function hasSignals() { return false; }
- function hasNamedParams() { return false; }
- function hasUserAndDbVariables() { return false; }
- function hasCompositeTypes() { return false; }
- function hasEnumTypes() {return false;}
- function hasReadOnlyQueries() { return false; }
- function hasFuncPrivs() { return false; }
- function hasServerAdminFuncs() { return false; }
- function hasRoles() { return false; }
- function hasAutovacuum() { return false; }
- function hasLocksView() { return false; }
- function hasPreparedXacts() { return false; }
- function hasDisableTriggers() { return false; }
- function hasAlterAggregate() { return false; }
- function hasAggregateSortOp() { return false; }
- function hasSharedComments() {return false;}
- function hasAnalyze() {return false;}
- function hasCreateTableLike() {return false;}
- function hasCreateTableLikeWithConstraints() {return false;}
- function hasCreateTableLikeWithIndexes() {return false;}
- function hasFTS() {return false;}
- function hasVirtualTransactionId() {return false;}
- function hasFunctionCosting() {return false;}
- function hasFunctionGUC() {return false;}
- function hasFunctionAlterSchema() { return false; }
- function hasFunctionAlterOwner() { return false; }
}
-
?>
diff --git a/classes/database/Postgres71.php b/classes/database/Postgres71.php
deleted file mode 100644
index 247409b3..00000000
--- a/classes/database/Postgres71.php
+++ /dev/null
@@ -1,511 +0,0 @@
-<?php
-
-/**
- * A class that implements the DB interface for Postgres
- * Note: This class uses ADODB and returns RecordSets.
- *
- * $Id: Postgres71.php,v 1.80 2007/12/11 14:17:17 ioguix Exp $
- */
-
-// @@@ THOUGHT: What about inherits? ie. use of ONLY???
-
-include_once('./classes/database/Postgres.php');
-
-class Postgres71 extends Postgres {
-
- var $major_version = 7.1;
- var $_lastSystemOID = 18539;
- var $_maxNameLen = 31;
-
- // List of all legal privileges that can be applied to different types
- // of objects.
- var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'ALL'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'ALL'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL')
- );
-
- // List of characters in acl lists and the privileges they
- // refer to.
- var $privmap = array(
- 'r' => 'SELECT',
- 'w' => 'UPDATE',
- 'a' => 'INSERT',
- 'd' => 'DELETE',
- 'R' => 'RULE',
- 'x' => 'REFERENCES',
- 't' => 'TRIGGER',
- 'X' => 'EXECUTE',
- 'U' => 'USAGE',
- 'C' => 'CREATE',
- 'T' => 'TEMPORARY'
- );
-
- // Function properties
- var $funcprops = array(array('', 'ISSTRICT'), array('', 'ISCACHABLE'));
- var $defaultprops = array('', '');
-
- // Select operators
- var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
- 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
- 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x');
- // Supported join operations for use with view wizard
- var $joinOps = array('INNER JOIN' => 'INNER JOIN', 'LEFT JOIN' => 'LEFT JOIN', 'RIGHT JOIN' => 'RIGHT JOIN', 'FULL JOIN' => 'FULL JOIN');
-
- /**
- * Constructor
- * @param $conn The database connection
- */
- function Postgres71($conn) {
- $this->Postgres($conn);
- }
-
- // Help functions
-
- function getHelpPages() {
- include_once('./help/PostgresDoc71.php');
- return $this->help_page;
- }
-
- /**
- * Sets the client encoding
- * @param $encoding The encoding to for the client
- * @return 0 success
- */
- function setClientEncoding($encoding) {
- $this->clean($encoding);
-
- $sql = "SET CLIENT_ENCODING TO '{$encoding}'";
-
- return $this->execute($sql);
- }
-
- // Database functions
-
- /**
- * Return all database available on the server
- * @return A list of databases, sorted alphabetically
- */
- function getDatabases($currentdatabase = NULL) {
- global $conf, $misc;
-
- $server_info = $misc->getServerInfo();
-
- if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
- $username = $server_info['username'];
- $this->clean($username);
- $clause = " AND pu.usename='{$username}'";
- }
- else $clause = '';
-
- if ($currentdatabase != NULL)
- $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
- else
- $orderby = "ORDER BY pdb.datname";
-
- if (!$conf['show_system'])
- $where = ' AND NOT pdb.datistemplate';
- else
- $where = ' AND pdb.datallowconn';
-
- $sql = "SELECT pdb.datname AS datname, pu.usename AS datowner, pg_encoding_to_char(encoding) AS datencoding,
- (SELECT description FROM pg_description pd WHERE pdb.oid=pd.objoid) AS datcomment
- FROM pg_database pdb, pg_user pu
- WHERE pdb.datdba = pu.usesysid
- {$where}
- {$clause}
- {$orderby}";
-
- return $this->selectSet($sql);
- }
-
- // Table functions
-
- /**
- * Protected method which alter a table
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $tblrs The table recordSet returned by getTable()
- * @param $name The new name for the table
- * @param $owner The new owner for the table
- * @param $schema The new schema for the table
- * @param $comment The comment on the table
- * @param $tablespace The new tablespace for the table ('' means leave as is)
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 tablespace error
- * @return -7 schema error
- */
- /* protected */
- function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
-
- $status = parent::_alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace);
- if ($status != 0)
- return $status;
-
- // if name != tablename, table has been renamed in parent
- $tablename = ($tblrs->fields['relname'] == $name) ? $tblrs->fields['relname'] : $name;
-
- /* $tablespace, schema not supported in pg71 */
- $this->fieldClean($owner);
-
- // Owner
- if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- $sql = "ALTER TABLE \"{$tablename}\" OWNER TO \"{$owner}\"";
-
- $status = $this->execute($sql);
- if ($status != 0) return -5;
- }
-
- return 0;
- }
-
- /**
- * Finds the number of rows that would be returned by a
- * query.
- * @param $query The SQL query
- * @param $count The count query
- * @return The count of rows
- * @return -1 error
- */
- function browseQueryCount($query, $count) {
- return $this->selectField($count, 'total');
- }
-
- /**
- * Retrieve the attribute definition of a table
- * @param $table The name of the table
- * @param $field (optional) The name of a field to return
- * @return All attributes in order
- */
- function getTableAttributes($table, $field = '') {
- $this->clean($table);
- $this->clean($field);
-
- if ($field == '') {
- $sql = "SELECT
- a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull,
- a.atthasdef, adef.adsrc, -1 AS attstattarget, a.attstorage, t.typstorage,
- false AS attisserial,
- (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
- FROM
- pg_attribute a LEFT JOIN pg_attrdef adef
- ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum,
- pg_class c,
- pg_type t
- WHERE
- c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
- ORDER BY a.attnum";
- }
- else {
- $sql = "SELECT
- a.attname, t.typname as type, t.typname as base_type,
- a.attlen, a.atttypmod, a.attnotnull,
- a.atthasdef, adef.adsrc, -1 AS attstattarget, a.attstorage, t.typstorage,
- (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
- FROM
- pg_attribute a LEFT JOIN pg_attrdef adef
- ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum,
- pg_class c,
- pg_type t
- WHERE
- c.relname = '{$table}' AND a.attname='{$field}' AND a.attrelid = c.oid AND a.atttypid = t.oid";
- }
-
- return $this->selectSet($sql);
- }
-
- /**
- * Formats a type correctly for display. This is a no-op in PostgreSQL 7.1+
- * @param $typname The name of the type
- * @param $typmod The contents of the typmod field
- */
- function formatType($typname, $typmod) {
- return $typname;
- }
-
- // View functions
-
- /**
- * Protected method which alter a view
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $vwrs The view recordSet returned by getView()
- * @param $name The new name for the view
- * @param $owner The new owner for the view
- * @param $comment The comment on the view
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 schema error
- */
- function _alterView($vwrs, $name, $owner, $schema, $comment) {
-
- $status = parent::_alterView($vwrs, $name, $owner, $schema, $comment);
- if ($status != 0)
- return $status;
-
- $this->fieldClean($name);
- // if name is not empty, view has been renamed in parent
- $view = (!empty($name)) ? $name : $tblrs->fields['relname'];
-
- $this->fieldClean($owner);
- // Owner
- if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) {
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- $sql = "ALTER TABLE \"{$view}\" OWNER TO \"{$owner}\"";
- $status = $this->execute($sql);
- if ($status != 0) return -5;
- }
-
- return 0;
- }
-
- // Sequence functions
-
- /**
- * Protected method which alter a sequence
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $seqrs The sequence recordSet returned by getSequence()
- * @param $name The new name for the sequence
- * @param $comment The comment on the sequence
- * @param $owner The new owner for the sequence
- * @param $schema The new schema for the sequence
- * @param $increment The increment
- * @param $minvalue The min value
- * @param $maxvalue The max value
- * @param $startvalue The starting value
- * @param $cachevalue The cache value
- * @param $cycledvalue True if cycled, false otherwise
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- */
- /*protected*/
- function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
-
- $status = parent::_alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue);
- if ($status != 0)
- return $status;
-
- /* $schema, $increment, $minvalue, $maxvalue, $startvalue, $cachevalue,
- * $cycledvalue not supported in pg71 */
- // if name != seqname, sequence has been renamed in parent
- $sequence = ($seqrs->fields['seqname'] == $name) ? $seqrs->fields['seqname'] : $name;
- $this->fieldClean($owner);
-
- // Owner
- if (!empty($owner)) {
-
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- if ($seqrs->fields['seqowner'] != $owner) {
- $sql = "ALTER TABLE \"{$sequence}\" OWNER TO \"{$owner}\"";
- $status = $this->execute($sql);
- if ($status != 0)
- return -5;
- }
- }
- return 0;
- }
-
- /**
- * Resets a given sequence to min value of sequence
- * @param $sequence Sequence name
- * @return 0 success
- * @return -1 sequence not found
- */
- function resetSequence($sequence) {
- // Get the minimum value of the sequence
- $seq = $this->getSequence($sequence);
- if ($seq->recordCount() != 1) return -1;
- $minvalue = $seq->fields['min_value'];
-
- /* This double-cleaning is deliberate */
- $this->fieldClean($sequence);
- $this->clean($sequence);
-
- $sql = "SELECT SETVAL('\"{$sequence}\"', {$minvalue}, FALSE)";
-
- return $this->execute($sql);
- }
-
- // Function functions
-
- /**
- * Returns all details for a particular function
- * @param $func The name of the function to retrieve
- * @return Function info
- */
- function getFunction($function_oid) {
- $this->clean($function_oid);
-
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- lanname as prolanguage,
- format_type(prorettype, NULL) as proresult,
- prosrc,
- probin,
- proretset,
- proisstrict,
- proiscachable,
- oidvectortypes(pc.proargtypes) AS proarguments,
- (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment
- FROM
- pg_proc pc, pg_language pl
- WHERE
- pc.oid = '$function_oid'::oid
- AND pc.prolang = pl.oid
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns an array containing a function's properties
- * @param $f The array of data for the function
- * @return An array containing the properties
- */
- function getFunctionProperties($f) {
- $temp = array();
-
- // Strict
- $f['proisstrict'] = $this->phpBool($f['proisstrict']);
- if ($f['proisstrict'])
- $temp[] = 'ISSTRICT';
- else
- $temp[] = '';
-
- // Cachable
- $f['proiscachable'] = $this->phpBool($f['proiscachable']);
- if ($f['proiscachable'])
- $temp[] = 'ISCACHABLE';
- else
- $temp[] = '';
-
- return $temp;
- }
-
- // Constraint functions
-
- /**
- * Returns a list of all constraints on a table
- * @param $table The table to find rules for
- * @return A recordset
- */
- function getConstraints($table) {
- $this->clean($table);
-
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $sql = "
- SELECT conname, consrc, contype, indkey FROM (
- SELECT
- rcname AS conname,
- 'CHECK (' || rcsrc || ')' AS consrc,
- 'c' AS contype,
- rcrelid AS relid,
- NULL AS indkey
- FROM
- pg_relcheck
- UNION ALL
- SELECT
- pc.relname,
- NULL,
- CASE WHEN indisprimary THEN
- 'p'
- ELSE
- 'u'
- END,
- pi.indrelid,
- indkey
- FROM
- pg_class pc,
- pg_index pi
- WHERE
- pc.oid=pi.indexrelid
- AND (pi.indisunique OR pi.indisprimary)
- ) AS sub
- WHERE relid = (SELECT oid FROM pg_class WHERE relname='{$table}')
- ORDER BY
- 1
- ";
-
- return $this->selectSet($sql);
- }
-
- // Trigger functions
-
- /**
- * Grabs a list of triggers on a table
- * @param $table The name of a table whose triggers to retrieve
- * @return A recordset
- */
- function getTriggers($table = '') {
- $this->clean($table);
-
- // We include constraint triggers
- $sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
- t.tgargs, t.tgnargs, t.tgconstrrelid,
- (SELECT relname FROM pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
- p.proname AS tgfname, c.relname, NULL AS tgdef
- FROM pg_trigger t LEFT JOIN pg_proc p
- ON t.tgfoid=p.oid, pg_class c
- WHERE t.tgrelid=c.oid
- AND c.relname='{$table}'";
-
- return $this->selectSet($sql);
- }
-
- // Aggregate functions
-
- /**
- * Gets all aggregates
- * @return A recordset
- */
- function getAggregates() {
- global $conf;
-
- if ($conf['show_system'])
- $where = '';
- else
- $where = "WHERE a.oid > '{$this->_lastSystemOID}'::oid";
-
- $sql = "
- SELECT
- a.aggname AS proname,
- CASE a.aggbasetype
- WHEN 0 THEN NULL
- ELSE format_type(a.aggbasetype, NULL)
- END AS proargtypes,
- (SELECT description FROM pg_description pd WHERE a.oid=pd.objoid) AS aggcomment
- FROM
- pg_aggregate a
- {$where}
- ORDER BY
- 1, 2;
- ";
-
- return $this->selectSet($sql);
- }
-
- // Capabilities
- function hasAlterTableOwner() { return true; }
- function hasAlterSequenceOwner() { return true; }
- function hasFullSubqueries() { return true; }
-
-}
-
-?>
diff --git a/classes/database/Postgres72.php b/classes/database/Postgres72.php
deleted file mode 100644
index d6898717..00000000
--- a/classes/database/Postgres72.php
+++ /dev/null
@@ -1,688 +0,0 @@
-<?php
-
-/**
- * A class that implements the DB interface for Postgres
- * Note: This class uses ADODB and returns RecordSets.
- *
- * $Id: Postgres72.php,v 1.94 2007/12/12 04:11:10 xzilla Exp $
- */
-
-
-include_once('./classes/database/Postgres71.php');
-
-class Postgres72 extends Postgres71 {
-
- var $major_version = 7.2;
-
- // Set the maximum built-in ID.
- var $_lastSystemOID = 16554;
-
- // List of all legal privileges that can be applied to different types
- // of objects.
- var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES')
- );
-
- // Extra "magic" types. BIGSERIAL was added in PostgreSQL 7.2.
- var $extraTypes = array('SERIAL', 'BIGSERIAL');
-
- /**
- * Constructor
- * @param $conn The database connection
- */
- function Postgres72($conn) {
- $this->Postgres71($conn);
-
- // Correct the error in the encoding tables, that was
- // fixed in PostgreSQL 7.2
- $this->codemap['LATIN5'] = 'ISO-8859-9';
- }
-
- // Help functions
-
- function getHelpPages() {
- include_once('./help/PostgresDoc72.php');
- return $this->help_page;
- }
-
- // User functions
-
- /**
- * Helper function that computes encypted PostgreSQL passwords
- * @param $username The username
- * @param $password The password
- */
- function _encryptPassword($username, $password) {
- return 'md5' . md5($password . $username);
- }
-
- /**
- * Changes a user's password
- * @param $username The username
- * @param $password The new password
- * @return 0 success
- */
- function changePassword($username, $password) {
- $enc = $this->_encryptPassword($username, $password);
- $this->fieldClean($username);
- $this->clean($enc);
-
- $sql = "ALTER USER \"{$username}\" WITH ENCRYPTED PASSWORD '{$enc}'";
-
- return $this->execute($sql);
- }
-
- /**
- * Creates a new user
- * @param $username The username of the user to create
- * @param $password A password for the user
- * @param $createdb boolean Whether or not the user can create databases
- * @param $createuser boolean Whether or not the user can create other users
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
- * @param $group (array) The groups to create the user in
- * @return 0 success
- */
- function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {
- $enc = $this->_encryptPassword($username, $password);
- $this->fieldClean($username);
- $this->clean($enc);
- $this->clean($expiry);
- $this->fieldArrayClean($groups);
-
- $sql = "CREATE USER \"{$username}\"";
- if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
- if (is_array($groups) && sizeof($groups) > 0) $sql .= " IN GROUP \"" . join('", "', $groups) . "\"";
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
- else $sql .= " VALID UNTIL 'infinity'";
-
- return $this->execute($sql);
- }
-
- /**
- * Adjusts a user's info
- * @param $username The username of the user to modify
- * @param $password A new password for the user
- * @param $createdb boolean Whether or not the user can create databases
- * @param $createuser boolean Whether or not the user can create other users
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
- * @return 0 success
- */
- function setUser($username, $password, $createdb, $createuser, $expiry) {
- $enc = $this->_encryptPassword($username, $password);
- $this->fieldClean($username);
- $this->clean($enc);
- $this->clean($expiry);
-
- $sql = "ALTER USER \"{$username}\"";
- if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";
- else $sql .= " VALID UNTIL 'infinity'";
-
- return $this->execute($sql);
- }
-
- /**
- * Returns all available process information.
- * @param $database (optional) Find only connections to specified database
- * @return A recordset
- */
- function getProcesses($database = null) {
- if ($database === null)
- $sql = "SELECT * FROM pg_stat_activity ORDER BY datname, usename, procpid";
- else {
- $this->clean($database);
- $sql = "SELECT * FROM pg_stat_activity WHERE datname='{$database}' ORDER BY usename, procpid";
- }
-
- return $this->selectSet($sql);
- }
-
- // Table functions
-
- /**
- * Returns the SQL for changing the current user
- * @param $user The user to change to
- * @return The SQL
- */
- function getChangeUserSQL($user) {
- $this->clean($user);
- return "SET SESSION AUTHORIZATION '{$user}';";
- }
-
- /**
- * Checks to see whether or not a table has a unique id column
- * @param $table The table name
- * @return True if it has a unique id, false otherwise
- * @return -99 error
- */
- function hasObjectID($table) {
- $this->clean($table);
-
- $sql = "SELECT relhasoids FROM pg_class WHERE relname='{$table}'";
-
- $rs = $this->selectSet($sql);
- if ($rs->recordCount() != 1) return -99;
- else {
- $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
- return $rs->fields['relhasoids'];
- }
- }
-
- /**
- * Returns table information
- * @param $table The name of the table
- * @return A recordset
- */
- function getTable($table) {
- $this->clean($table);
-
- $sql = "SELECT pc.relname,
- pg_get_userbyid(pc.relowner) AS relowner,
- (SELECT description FROM pg_description pd
- WHERE pc.oid=pd.objoid AND objsubid = 0) AS relcomment
- FROM pg_class pc
- WHERE pc.relname='{$table}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Return all tables in current database
- * @param $all True to fetch all tables, false for just in current schema
- * @return All tables, sorted alphabetically
- */
- function getTables($all = false) {
- global $conf;
- if (!$conf['show_system'] || $all) $where = "AND c.relname NOT LIKE 'pg\\\\_%' ";
- else $where = '';
-
- $sql = "SELECT NULL AS nspname, c.relname,
- (SELECT usename FROM pg_user u WHERE u.usesysid=c.relowner) AS relowner,
- (SELECT description FROM pg_description pd WHERE c.oid=pd.objoid AND objsubid = 0) AS relcomment,
- reltuples::numeric
- FROM pg_class c WHERE c.relkind='r' {$where}ORDER BY relname";
- return $this->selectSet($sql);
- }
-
- /**
- * Retrieve the attribute definition of a table
- * @param $table The name of the table
- * @param $field (optional) The name of a field to return
- * @return All attributes in order
- */
- function getTableAttributes($table, $field = '') {
- $this->clean($table);
- $this->clean($field);
-
- if ($field == '') {
- $sql = "
- SELECT
- a.attname,
- format_type(a.atttypid, a.atttypmod) as type, a.atttypmod,
- a.attnotnull, a.atthasdef, adef.adsrc,
- -1 AS attstattarget, a.attstorage, t.typstorage, false AS attisserial,
- description as comment
- FROM
- pg_attribute a LEFT JOIN pg_attrdef adef
- ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum
- LEFT JOIN pg_type t ON a.atttypid=t.oid
- LEFT JOIN pg_description d ON (a.attrelid = d.objoid AND a.attnum = d.objsubid)
- WHERE
- a.attrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
- AND a.attnum > 0
- ORDER BY a.attnum";
- }
- else {
- $sql = "
- SELECT
- a.attname,
- format_type(a.atttypid, a.atttypmod) as type,
- format_type(a.atttypid, NULL) as base_type,
- a.atttypmod,
- a.attnotnull, a.atthasdef, adef.adsrc,
- -1 AS attstattarget, a.attstorage, t.typstorage,
- description as comment
- FROM
- pg_attribute a LEFT JOIN pg_attrdef adef
- ON a.attrelid=adef.adrelid AND a.attnum=adef.adnum
- LEFT JOIN pg_type t ON a.atttypid=t.oid
- LEFT JOIN pg_description d ON (a.attrelid = d.objoid AND a.attnum = d.objsubid)
- WHERE
- a.attrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
- AND a.attname = '{$field}'";
- }
-
- return $this->selectSet($sql);
- }
-
- // View functions
-
- /**
- * Returns a list of all views in the database
- * @return All views
- */
- function getViews() {
- global $conf;
-
- if (!$conf['show_system'])
- $where = " WHERE viewname NOT LIKE 'pg\\\\_%'";
- else
- $where = '';
-
- $sql = "SELECT viewname AS relname, viewowner AS relowner, definition AS vwdefinition,
- (SELECT description FROM pg_description pd, pg_class pc
- WHERE pc.oid=pd.objoid AND pc.relname=v.viewname AND pd.objsubid = 0) AS relcomment
- FROM pg_views v
- {$where}
- ORDER BY relname";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all details for a particular view
- * @param $view The name of the view to retrieve
- * @return View info
- */
- function getView($view) {
- $this->clean($view);
-
- $sql = "SELECT viewname AS relname, NULL AS nspname, viewowner AS relowner, definition AS vwdefinition,
- (SELECT description FROM pg_description pd, pg_class pc
- WHERE pc.oid=pd.objoid AND pc.relname=v.viewname AND pd.objsubid = 0) AS relcomment
- FROM pg_views v
- WHERE viewname='{$view}'";
-
- return $this->selectSet($sql);
- }
-
- // Constraint functions
-
- /**
- * Removes a constraint from a relation
- * @param $constraint The constraint to drop
- * @param $relation The relation from which to drop
- * @param $type The type of constraint (c, f, u or p)
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- * @return -99 dropping foreign keys not supported
- */
- function dropConstraint($constraint, $relation, $type, $cascade) {
- $this->fieldClean($constraint);
- $this->fieldClean($relation);
-
- switch ($type) {
- case 'c':
- // CHECK constraint
- $sql = "ALTER TABLE \"{$relation}\" DROP CONSTRAINT \"{$constraint}\" RESTRICT";
-
- return $this->execute($sql);
- break;
- case 'p':
- case 'u':
- // PRIMARY KEY or UNIQUE constraint
- return $this->dropIndex($constraint, $cascade);
- break;
- case 'f':
- // FOREIGN KEY constraint
- return -99;
- }
- }
-
- /**
- * Adds a unique constraint to a table
- * @param $table The table to which to add the unique key
- * @param $fields (array) An array of fields over which to add the unique key
- * @param $name (optional) The name to give the key, otherwise default name is assigned
- * @param $tablespace (optional) The tablespace for the schema, '' indicates default.
- * @return 0 success
- * @return -1 no fields given
- */
- function addUniqueKey($table, $fields, $name = '', $tablespace = '') {
- if (!is_array($fields) || sizeof($fields) == 0) return -1;
- $this->fieldClean($table);
- $this->fieldArrayClean($fields);
- $this->fieldClean($name);
- $this->fieldClean($tablespace);
-
- $schema = $this->schema();
-
- $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "UNIQUE (\"" . join('","', $fields) . "\")";
-
- if ($tablespace != '' && $this->hasTablespaces())
- $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
-
- return $this->execute($sql);
- }
-
- /**
- * Adds a primary key constraint to a table
- * @param $table The table to which to add the primery key
- * @param $fields (array) An array of fields over which to add the primary key
- * @param $name (optional) The name to give the key, otherwise default name is assigned
- * @param $tablespace (optional) The tablespace for the schema, '' indicates default.
- * @return 0 success
- * @return -1 no fields given
- */
- function addPrimaryKey($table, $fields, $name = '', $tablespace = '') {
- if (!is_array($fields) || sizeof($fields) == 0) return -1;
- $this->fieldClean($table);
- $this->fieldArrayClean($fields);
- $this->fieldClean($name);
- $this->fieldClean($tablespace);
-
- $schema = $this->schema();
-
- $sql = "ALTER TABLE {$schema}\"{$table}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "PRIMARY KEY (\"" . join('","', $fields) . "\")";
-
- if ($tablespace != '' && $this->hasTablespaces())
- $sql .= " USING INDEX TABLESPACE \"{$tablespace}\"";
-
- return $this->execute($sql);
- }
-
- // Function functions
-
- /**
- * Returns a list of all functions in the database
- * @param $all If true, will find all available functions, if false just userland ones
- * @return All functions
- */
- function getFunctions($all = false) {
- if ($all)
- $where = '';
- else
- $where = "AND p.oid > '{$this->_lastSystemOID}'";
-
- $sql = "SELECT
- p.oid AS prooid,
- p.proname,
- false AS proretset,
- format_type(p.prorettype, NULL) AS proresult,
- oidvectortypes(p.proargtypes) AS proarguments,
- pl.lanname AS prolanguage,
- (SELECT description FROM pg_description pd WHERE p.oid=pd.objoid) AS procomment,
- p.proname || ' (' || oidvectortypes(p.proargtypes) || ')' AS proproto,
- format_type(p.prorettype, NULL) AS proreturns
- FROM
- pg_proc p, pg_language pl
- WHERE
- p.prolang = pl.oid AND
- (pronargs = 0 OR oidvectortypes(p.proargtypes) <> '')
- {$where}
- ORDER BY
- p.proname, proresult
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Updates (replaces) a function.
- * @param $function_oid The OID of the function
- * @param $funcname The name of the function to create
- * @param $newname The new name for the function
- * @param $args The array of argument types
- * @param $returns The return type
- * @param $definition The definition for the new function
- * @param $language The language the function is written for
- * @param $flags An array of optional flags
- * @param $setof True if returns a set, false otherwise
- * @param $comment The comment on the function
- * @return 0 success
- * @return -1 transaction error
- * @return -2 drop function error
- * @return -3 create function error
- * @return -4 comment error
- */
- function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $rows, $cost, $comment) {
- // Begin a transaction
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Replace the existing function
- if ($funcname != $newname) {
- $status = $this->dropFunction($function_oid, false);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
-
- $status = $this->createFunction($newname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, false);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- } else {
- $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, true);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
-
- // Comment on the function
- $this->fieldClean($newname);
- $this->clean($comment);
- $status = $this->setComment('FUNCTION', "\"{$newname}\"({$args})", null, $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- return $this->endTransaction();
- }
-
- // Type functions
-
- /**
- * Returns a list of all types in the database
- * @param $all If true, will find all available functions, if false just those in search path
- * @param $tabletypes If true, will include table types
- * @param $domains Ignored
- * @return A recordet
- */
- function getTypes($all = false, $tabletypes = false, $domains = false) {
- global $conf;
-
- if ($all || $conf['show_system']) {
- $where = '';
- } else {
- $where = "AND pt.oid > '{$this->_lastSystemOID}'::oid";
- }
- // Never show system table types
- $where2 = "AND c.oid > '{$this->_lastSystemOID}'::oid";
-
- // Create type filter
- $tqry = "'c'";
- if ($tabletypes)
- $tqry .= ", 'r', 'v'";
-
- $sql = "SELECT
- pt.typname AS basename,
- format_type(pt.oid, NULL) AS typname,
- pu.usename AS typowner,
- (SELECT description FROM pg_description pd WHERE pt.oid=pd.objoid) AS typcomment
- FROM
- pg_type pt,
- pg_user pu
- WHERE
- pt.typowner = pu.usesysid
- AND (pt.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_class c WHERE c.oid = pt.typrelid {$where2}))
- AND typname !~ '^_'
- {$where}
- ORDER BY typname
- ";
-
- return $this->selectSet($sql);
- }
-
- // Opclass functions
-
- /**
- * Gets all opclasses
- * @return A recordset
- */
- function getOpClasses() {
- global $conf;
-
- if ($conf['show_system'])
- $where = '';
- else
- $where = "AND po.oid > '{$this->_lastSystemOID}'::oid";
-
- $sql = "
- SELECT DISTINCT
- pa.amname,
- po.opcname,
- format_type(po.opcintype, NULL) AS opcintype,
- TRUE AS opcdefault,
- NULL::text AS opccomment
- FROM
- pg_opclass po, pg_am pa
- WHERE
- po.opcamid=pa.oid
- {$where}
- ORDER BY 1,2
- ";
-
- return $this->selectSet($sql);
- }
-
- // Administration functions
-
- /**
- * Vacuums a database
- * @param $table The table to vacuum
- * @param $analyze If true, also does analyze
- * @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
- * @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
- */
- function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) {
- $sql = "VACUUM";
- if ($full) $sql .= " FULL";
- if ($freeze) $sql .= " FREEZE";
- if ($analyze) $sql .= " ANALYZE";
- if ($table != '') {
- $this->fieldClean($table);
- $sql .= " \"{$table}\"";
- }
-
- return $this->execute($sql);
- }
-
- /**
- * Analyze a database
- * @note PostgreSQL 7.2 finally had an independent ANALYZE command
- * @param $table (optional) The table to analyze
- */
- function analyzeDB($table = '') {
- if ($table != '') {
- $this->fieldClean($table);
- $sql = "ANALYZE \"{$table}\"";
- }
- else
- $sql = "ANALYZE";
-
- return $this->execute($sql);
- }
-
- // Statistics collector functions
-
- /**
- * Fetches statistics for a database
- * @param $database The database to fetch stats for
- * @return A recordset
- */
- function getStatsDatabase($database) {
- $this->clean($database);
-
- $sql = "SELECT * FROM pg_stat_database WHERE datname='{$database}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Fetches tuple statistics for a table
- * @param $table The table to fetch stats for
- * @return A recordset
- */
- function getStatsTableTuples($table) {
- $this->clean($table);
-
- $sql = 'SELECT * FROM pg_stat_all_tables WHERE';
- if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
- $sql .= " relname='{$table}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Fetches I/0 statistics for a table
- * @param $table The table to fetch stats for
- * @return A recordset
- */
- function getStatsTableIO($table) {
- $this->clean($table);
-
- $sql = 'SELECT * FROM pg_statio_all_tables WHERE';
- if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
- $sql .= " relname='{$table}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Fetches tuple statistics for all indexes on a table
- * @param $table The table to fetch index stats for
- * @return A recordset
- */
- function getStatsIndexTuples($table) {
- $this->clean($table);
-
- $sql = 'SELECT * FROM pg_stat_all_indexes WHERE';
- if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
- $sql .= " relname='{$table}' ORDER BY indexrelname";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Fetches I/0 statistics for all indexes on a table
- * @param $table The table to fetch index stats for
- * @return A recordset
- */
- function getStatsIndexIO($table) {
- $this->clean($table);
-
- $sql = 'SELECT * FROM pg_statio_all_indexes WHERE';
- if ($this->hasSchemas()) $sql .= " schemaname='{$this->_schema}' AND";
- $sql .= " relname='{$table}' ORDER BY indexrelname";
-
- return $this->selectSet($sql);
- }
-
- // Capabilities
- function hasWithoutOIDs() { return true; }
- function hasPartialIndexes() { return true; }
- function hasProcesses() { return true; }
- function hasStatsCollector() { return true; }
- function hasFullVacuum() { return true; }
- function hasAnalyze() { return true; }
-
-}
-
-?>
diff --git a/classes/database/Postgres73.php b/classes/database/Postgres73.php
index 98811e58..d72ad8bf 100644
--- a/classes/database/Postgres73.php
+++ b/classes/database/Postgres73.php
@@ -9,50 +9,20 @@
// @@@ THOUGHT: What about inherits? ie. use of ONLY???
-include_once('./classes/database/Postgres72.php');
+include_once('./classes/database/Postgres74.php');
-class Postgres73 extends Postgres72 {
+class Postgres73 extends Postgres74 {
var $major_version = 7.3;
-
- // Store the current schema
- var $_schema;
-
- // Last oid assigned to a system object
- var $_lastSystemOID = 16974;
-
- // Max object name length
- var $_maxNameLen = 63;
-
- // List of all legal privileges that can be applied to different types
- // of objects.
- var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
- 'database' => array('CREATE', 'TEMPORARY', 'ALL PRIVILEGES'),
- 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
- 'language' => array('USAGE', 'ALL PRIVILEGES'),
- 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES')
- );
- // Function properties
- var $funcprops = array( array('', 'VOLATILE', 'IMMUTABLE', 'STABLE'),
- array('', 'CALLED ON NULL INPUT', 'RETURNS NULL ON NULL INPUT'),
- array('', 'SECURITY INVOKER', 'SECURITY DEFINER'));
- var $defaultprops = array('', '', '');
-
- // Select operators
- var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
- 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', 'SIMILAR TO' => 'i',
- 'NOT SIMILAR TO' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
- 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x');
+ // How often to execute the trigger
+ var $triggerFrequency = array('ROW');
/**
* Constructor
* @param $conn The database connection
*/
function Postgres73($conn) {
- $this->Postgres72($conn);
+ $this->Postgres74($conn);
}
// Help functions
@@ -62,70 +32,7 @@ class Postgres73 extends Postgres72 {
return $this->help_page;
}
- /**
- * Returns the current schema to prepend on object names
- */
- function schema() {
- return "\"{$this->_schema}\".";
- }
-
- // Schema functions
-
- /**
- * Sets the current working schema. Will also set class variable.
- * @param $schema The the name of the schema to work in
- * @return 0 success
- */
- function setSchema($schema) {
- // Get the current schema search path, including 'pg_catalog'.
- $search_path = $this->getSearchPath();
- // Prepend $schema to search path
- array_unshift($search_path, $schema);
- $status = $this->setSearchPath($search_path);
- if ($status == 0) {
- $this->clean($schema);
- $this->_schema = $schema;
- return 0;
- }
- else return $status;
- }
-
- /**
- * Sets the current schema search path
- * @param $paths An array of schemas in required search order
- * @return 0 success
- * @return -1 Array not passed
- * @return -2 Array must contain at least one item
- */
- function setSearchPath($paths) {
- if (!is_array($paths)) return -1;
- elseif (sizeof($paths) == 0) return -2;
- elseif (sizeof($paths) == 1 && $paths[0] == '') {
- // Need to handle empty paths in some cases
- $paths[0] = 'pg_catalog';
- }
-
- // Loop over all the paths to check that none are empty
- $temp = array();
- foreach ($paths as $schema) {
- if ($schema != '') $temp[] = $schema;
- }
- $this->fieldArrayClean($temp);
-
- $sql = 'SET SEARCH_PATH TO "' . implode('","', $temp) . '"';
-
- return $this->execute($sql);
- }
-
- /**
- * Return the current schema search path
- * @return Array of schema names
- */
- function getSearchPath() {
- $sql = 'SELECT current_schemas(false) AS search_path';
-
- return $this->phpArray($this->selectField($sql, 'search_path'));
- }
+ // Database functions
/**
* Return all schemas in the current database
@@ -143,170 +50,43 @@ class Postgres73 extends Postgres72 {
}
}
else $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
- $sql = "SELECT pn.nspname, pu.usename AS nspowner, pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
+ $sql = "
+ SELECT pn.nspname, pu.usename AS nspowner,
+ pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
FROM pg_catalog.pg_namespace pn LEFT JOIN pg_catalog.pg_user pu ON (pn.nspowner = pu.usesysid)
- {$where} ORDER BY nspname";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Return all information relating to a schema
- * @param $schema The name of the schema
- * @return Schema information
- */
- function getSchemaByName($schema) {
- $this->clean($schema);
- $sql = "SELECT nspname, nspowner, nspacl, pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
- FROM pg_catalog.pg_namespace pn
- WHERE nspname='{$schema}'";
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a new schema.
- * @param $schemaname The name of the schema to create
- * @param $authorization (optional) The username to create the schema for.
- * @param $comment (optional) If omitted, defaults to nothing
- * @return 0 success
- */
- function createSchema($schemaname, $authorization = '', $comment = '') {
- $this->fieldClean($schemaname);
- $this->fieldClean($authorization);
- $this->clean($comment);
-
- $sql = "CREATE SCHEMA \"{$schemaname}\"";
- if ($authorization != '') $sql .= " AUTHORIZATION \"{$authorization}\"";
-
- if ($comment != '') {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
- }
-
- // Create the new schema
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Set the comment
- if ($comment != '') {
- $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- return $this->endTransaction();
- }
-
- return 0;
- }
-
- /**
- * Drops a schema.
- * @param $schemaname The name of the schema to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropSchema($schemaname, $cascade) {
- $this->fieldClean($schemaname);
-
- $sql = "DROP SCHEMA \"{$schemaname}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Updates a schema.
- * @param $schemaname The name of the schema to drop
- * @param $comment The new comment for this schema
- * @return 0 success
- */
- function updateSchema($schemaname, $comment, $name) {
- $this->fieldClean($schemaname);
- $this->fieldClean($name);
- $this->clean($comment);
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- $status = $this->setComment('SCHEMA', $schemaname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Only if the name has changed
- if ($name != $schemaname) {
- $sql = "ALTER SCHEMA \"{$schemaname}\" RENAME TO \"{$name}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- return $this->endTransaction();
- }
-
- /**
- * Returns the specified variable information.
- * @return the field
- */
- function getVariable($setting) {
- $sql = "SHOW $setting";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all available variable information.
- * @return A recordset
- */
- function getVariables() {
- $sql = "SHOW ALL";
+ {$where}
+ ORDER BY nspname";
return $this->selectSet($sql);
}
- /**
- * Returns all available process information.
- * @param $database (optional) Find only connections to specified database
- * @return A recordset
- */
- function getProcesses($database = null) {
- if ($database === null)
- $sql = "SELECT * FROM pg_catalog.pg_stat_activity ORDER BY datname, usename, procpid";
- else {
- $this->clean($database);
- $sql = "SELECT * FROM pg_catalog.pg_stat_activity WHERE datname='{$database}' ORDER BY usename, procpid";
- }
-
- return $this->selectSet($sql);
- }
+ // Trigger functions
/**
- * Returns table locks information in the current database
+ * Grabs a list of triggers on a table
+ * @param $table The name of a table whose triggers to retrieve
* @return A recordset
*/
- function getLocks() {
- global $conf;
-
- if (!$conf['show_system'])
- $where = "AND pn.nspname NOT LIKE 'pg\\\\_%'";
- else
- $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
+ function getTriggers($table = '') {
+ $this->clean($table);
- $sql = "SELECT pn.nspname, pc.relname AS tablename, pl.transaction, pl.pid, pl.mode, pl.granted
- FROM pg_catalog.pg_locks pl, pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
- WHERE pl.relation = pc.oid AND pc.relnamespace=pn.oid {$where}
- ORDER BY nspname,tablename";
+ $sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
+ t.tgargs, t.tgnargs, t.tgconstrrelid,
+ (SELECT relname FROM pg_catalog.pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
+ p.proname AS tgfname, c.relname, NULL AS tgdef,
+ p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
+ ns.nspname AS pronamespace
+ FROM pg_catalog.pg_namespace ns,
+ pg_catalog.pg_trigger t LEFT JOIN pg_catalog.pg_proc p
+ ON t.tgfoid=p.oid, pg_catalog.pg_class c
+ WHERE t.tgrelid=c.oid
+ AND c.relname='{$table}'
+ AND c.relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
+ AND (NOT tgisconstraint OR NOT EXISTS
+ (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
+ AND p.pronamespace = ns.oid";
return $this->selectSet($sql);
}
@@ -314,130 +94,6 @@ class Postgres73 extends Postgres72 {
// Table functions
/**
- * Checks to see whether or not a table has a unique id column
- * @param $table The table name
- * @return True if it has a unique id, false otherwise
- * @return -99 error
- */
- function hasObjectID($table) {
- $this->clean($table);
-
- $sql = "SELECT relhasoids FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
-
- $rs = $this->selectSet($sql);
- if ($rs->recordCount() != 1) return -99;
- else {
- $rs->fields['relhasoids'] = $this->phpBool($rs->fields['relhasoids']);
- return $rs->fields['relhasoids'];
- }
- }
-
- /**
- * Protected method which alter a table
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $tblrs The table recordSet returned by getTable()
- * @param $name The new name for the table
- * @param $owner The new owner for the table
- * @param $schema The new schema for the table
- * @param $comment The comment on the table
- * @param $tablespace The new tablespace for the table ('' means leave as is)
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 tablespace error
- * @return -7 schema error
- */
- /* protected */
- function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
-
- $this->fieldClean($name);
- $this->fieldClean($owner);
- $this->clean($comment);
- /* $schema, $owner, $tablespace not supported in pg70 */
-
- $table = $tblrs->fields['relname'];
-
- // Comment
- $status = $this->setComment('TABLE', '', $table, $comment);
- if ($status != 0) return -4;
-
- // Rename (only if name has changed)
- if ($name != $table) {
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" RENAME TO \"{$name}\"";
- $status = $this->execute($sql);
- if ($status != 0)
- return -3;
- $table = $name;
- }
-
- // Owner
- if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" OWNER TO \"{$owner}\"";
-
- $status = $this->execute($sql);
- if ($status != 0) return -5;
- }
-
- return 0;
- }
-
- /**
- * Removes a table from the database
- * @param $table The table to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropTable($table, $cascade) {
- $this->fieldClean($table);
-
- $sql = "DROP TABLE \"{$this->_schema}\".\"{$table}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Given an array of attnums and a relation, returns an array mapping
- * attribute number to attribute name.
- * @param $table The table to get attributes for
- * @param $atts An array of attribute numbers
- * @return An array mapping attnum to attname
- * @return -1 $atts must be an array
- * @return -2 wrong number of attributes found
- */
- function getAttributeNames($table, $atts) {
- $this->clean($table);
- $this->arrayClean($atts);
-
- if (!is_array($atts)) return -1;
-
- if (sizeof($atts) == 0) return array();
-
- $sql = "SELECT attnum, attname FROM pg_catalog.pg_attribute WHERE
- attrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
- relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
- AND attnum IN ('" . join("','", $atts) . "')";
-
- $rs = $this->selectSet($sql);
- if ($rs->recordCount() != sizeof($atts)) {
- return -2;
- }
- else {
- $temp = array();
- while (!$rs->EOF) {
- $temp[$rs->fields['attnum']] = $rs->fields['attname'];
- $rs->moveNext();
- }
- return $temp;
- }
- }
-
- /**
* Get the fields for uniquely identifying a row in a table
* @param $table The table for which to retrieve the identifier
* @return An array mapping attribute number to attribute name, empty for no identifiers
@@ -483,437 +139,7 @@ class Postgres73 extends Postgres72 {
}
}
- /**
- * Returns table information
- * @param $table The name of the table
- * @return A recordset
- */
- function getTable($table) {
- $this->clean($table);
-
- $sql = "
- SELECT
- c.relname, n.nspname, u.usename AS relowner,
- pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind = 'r'
- AND n.nspname = '{$this->_schema}'
- AND c.relname = '{$table}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Return all tables in current database (and schema)
- * @param $all True to fetch all tables, false for just in current schema
- * @return All tables, sorted alphabetically
- */
- function getTables($all = false) {
- if ($all) {
- // Exclude pg_catalog and information_schema tables
- $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
- FROM pg_catalog.pg_tables
- WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
- ORDER BY schemaname, tablename";
- } else {
- $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
- pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
- reltuples::bigint
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind = 'r'
- AND nspname='{$this->_schema}'
- ORDER BY c.relname";
- }
-
- return $this->selectSet($sql);
- }
-
- /**
- * Retrieve the attribute definition of a table
- * @param $table The name of the table
- * @param $field (optional) The name of a field to return
- * @return All attributes in order
- */
- function getTableAttributes($table, $field = '') {
- $this->clean($table);
- $this->clean($field);
-
- if ($field == '') {
- // This query is made much more complex by the addition of the 'attisserial' field.
- // The subquery to get that field checks to see if there is an internally dependent
- // sequence on the field.
- $sql = "
- SELECT
- a.attname,
- pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
- a.atttypmod,
- a.attnotnull, a.atthasdef, adef.adsrc,
- a.attstattarget, a.attstorage, t.typstorage,
- (
- SELECT 1 FROM pg_catalog.pg_depend pd, pg_catalog.pg_class pc
- WHERE pd.objid=pc.oid
- AND pd.classid=pc.tableoid
- AND pd.refclassid=pc.tableoid
- AND pd.refobjid=a.attrelid
- AND pd.refobjsubid=a.attnum
- AND pd.deptype='i'
- AND pc.relkind='S'
- ) IS NOT NULL AS attisserial,
- pg_catalog.col_description(a.attrelid, a.attnum) AS comment
-
- FROM
- pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
- ON a.attrelid=adef.adrelid
- AND a.attnum=adef.adnum
- LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
- WHERE
- a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
- nspname = '{$this->_schema}'))
- AND a.attnum > 0 AND NOT a.attisdropped
- ORDER BY a.attnum";
- }
- else {
- $sql = "
- SELECT
- a.attname,
- pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
- pg_catalog.format_type(a.atttypid, NULL) as base_type,
- a.atttypmod,
- a.attnotnull, a.atthasdef, adef.adsrc,
- a.attstattarget, a.attstorage, t.typstorage,
- pg_catalog.col_description(a.attrelid, a.attnum) AS comment
- FROM
- pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef
- ON a.attrelid=adef.adrelid
- AND a.attnum=adef.adnum
- LEFT JOIN pg_catalog.pg_type t ON a.atttypid=t.oid
- WHERE
- a.attrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
- nspname = '{$this->_schema}'))
- AND a.attname = '{$field}'";
- }
-
- return $this->selectSet($sql);
- }
-
- /**
- * Sets default value of a column
- * @param $table The table from which to drop
- * @param $column The column name to set
- * @param $default The new default value
- * @return 0 success
- */
- function setColumnDefault($table, $column, $default) {
- $this->fieldClean($table);
- $this->fieldClean($column);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops default value of a column
- * @param $table The table from which to drop
- * @param $column The column name to drop default
- * @return 0 success
- */
- function dropColumnDefault($table, $column) {
- $this->fieldClean($table);
- $this->fieldClean($column);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops a column from a table
- * @param $table The table from which to drop a column
- * @param $column The column to be dropped
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropColumn($table, $column, $cascade) {
- $this->fieldClean($table);
- $this->fieldClean($column);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" DROP COLUMN \"{$column}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Renames a column in a table
- * @param $table The table containing the column to be renamed
- * @param $column The column to be renamed
- * @param $newName The new name for the column
- * @return 0 success
- */
- function renameColumn($table, $column, $newName) {
- $this->fieldClean($table);
- $this->fieldClean($column);
- $this->fieldClean($newName);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";
-
- return $this->execute($sql);
- }
-
- /**
- * Sets whether or not a column can contain NULLs
- * @param $table The table that contains the column
- * @param $column The column to alter
- * @param $state True to set null, false to set not null
- * @return 0 success
- */
- function setColumnNull($table, $column, $state) {
- $this->fieldClean($table);
- $this->fieldClean($column);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . (($state) ? 'DROP' : 'SET') . " NOT NULL";
-
- return $this->execute($sql);
- }
-
- // Row functions
-
- /**
- * Empties a table in the database
- * @param $table The table to be emptied
- * @return 0 success
- */
- function emptyTable($table) {
- $this->fieldClean($table);
-
- $sql = "DELETE FROM \"{$this->_schema}\".\"{$table}\"";
-
- return $this->execute($sql);
- }
-
- /**
- * Adds a check constraint to a table
- * @param $table The table to which to add the check
- * @param $definition The definition of the check
- * @param $name (optional) The name to give the check, otherwise default name is assigned
- * @return 0 success
- */
- function addCheckConstraint($table, $definition, $name = '') {
- $this->fieldClean($table);
- $this->fieldClean($name);
- // @@ How the heck do you clean a definition???
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "CHECK ({$definition})";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops a check constraint from a table
- * @param $table The table from which to drop the check
- * @param $name The name of the check to be dropped
- * @return 0 success
- * @return -2 transaction error
- * @return -3 lock error
- * @return -4 check drop error
- */
- function dropCheckConstraint($table, $name) {
- $this->clean($table);
- $this->clean($name);
-
- // Begin transaction
- $status = $this->beginTransaction();
- if ($status != 0) return -2;
-
- // Properly lock the table
- $sql = "LOCK TABLE \"{$this->_schema}\".\"{$table}\" IN ACCESS EXCLUSIVE MODE";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
-
- // Delete the check constraint
- $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
- nspname = '{$this->_schema}')) AND rcname='{$name}'";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- // Update the pg_class catalog to reflect the new number of checks
- $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE
- rcrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
- nspname = '{$this->_schema}')))
- WHERE relname='{$table}'";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- // Otherwise, close the transaction
- return $this->endTransaction();
- }
-
- // Administration functions
-
- /**
- * Vacuums a database
- * @param $table The table to vacuum
- * @param $analyze If true, also does analyze
- * @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
- * @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
- */
- function vacuumDB($table = '', $analyze = false, $full = false, $freeze = false) {
-
- $sql = "VACUUM";
- if ($full) $sql .= " FULL";
- if ($freeze) $sql .= " FREEZE";
- if ($analyze) $sql .= " ANALYZE";
- if ($table != '') {
- $this->fieldClean($table);
- $sql .= " \"{$this->_schema}\".\"{$table}\"";
- }
-
- return $this->execute($sql);
- }
-
- /**
- * Analyze a database
- * @note PostgreSQL 7.2 finally had an independent ANALYZE command
- * @param $table (optional) The table to analyze
- */
- function analyzeDB($table = '') {
- if ($table != '') {
- $this->fieldClean($table);
-
- $sql = "ANALYZE \"{$this->_schema}\".\"{$table}\"";
- }
- else
- $sql = "ANALYZE";
-
- return $this->execute($sql);
- }
-
- // Inheritance functions
-
- /**
- * Finds the names and schemas of parent tables (in order)
- * @param $table The table to find the parents for
- * @return A recordset
- */
- function getTableParents($table) {
- $this->clean($table);
-
- $sql = "
- SELECT
- pn.nspname, relname
- FROM
- pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
- WHERE
- pc.oid=pi.inhparent
- AND pc.relnamespace=pn.oid
- AND pi.inhrelid = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
- ORDER BY
- pi.inhseqno
- ";
-
- return $this->selectSet($sql);
- }
-
-
- /**
- * Finds the names and schemas of child tables
- * @param $table The table to find the children for
- * @return A recordset
- */
- function getTableChildren($table) {
- $this->clean($table);
-
- $sql = "
- SELECT
- pn.nspname, relname
- FROM
- pg_catalog.pg_class pc, pg_catalog.pg_inherits pi, pg_catalog.pg_namespace pn
- WHERE
- pc.oid=pi.inhrelid
- AND pc.relnamespace=pn.oid
- AND pi.inhparent = (SELECT oid from pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname = '{$this->_schema}'))
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Protected method which alter a view
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $vwrs The view recordSet returned by getView()
- * @param $name The new name for the view
- * @param $owner The new owner for the view
- * @param $comment The comment on the view
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 schema error
- */
- /*protected*/
- function _alterView($vwrs, $name, $owner, $schema, $comment) {
-
- $view = $vwrs->fields['relname'];
-
- // Comment
- $this->clean($comment);
- if ($this->setComment('VIEW', $view, '', $comment) != 0)
- return -4;
-
- // Owner
- $this->fieldClean($owner);
- if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) {
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$view}\" OWNER TO \"{$owner}\"";
- $status = $this->execute($sql);
- if ($status != 0) return -5;
- }
-
- // Rename (only if name has changed)
- $this->fieldClean($name);
- if ($name != $view) {
- if ($this->renameView($view, $name) != 0)
- return -3;
- }
-
- return 0;
- }
-
- /**
- * Returns a list of all views in the database
- * @return All views
- */
- function getViews() {
- $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
- pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
- FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
- WHERE (n.nspname='{$this->_schema}') AND (c.relkind = 'v'::\"char\") ORDER BY relname";
-
- return $this->selectSet($sql);
- }
+ // View function
/**
* Returns all details for a particular view
@@ -923,231 +149,65 @@ class Postgres73 extends Postgres72 {
function getView($view) {
$this->clean($view);
- $sql = "SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
- pg_catalog.pg_get_viewdef(c.oid) AS vwdefinition, pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
- FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
- WHERE (c.relname = '$view')
- AND n.nspname='{$this->_schema}'";
+ $sql = "
+ SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.pg_get_viewdef(c.oid) AS vwdefinition, pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
+ FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
+ WHERE (c.relname = '$view')
+ AND n.nspname='{$this->_schema}'";
return $this->selectSet($sql);
}
- /**
- * Updates a view.
- * @param $viewname The name fo the view to update
- * @param $definition The new definition for the view
- * @return 0 success
- * @return -1 transaction error
- * @return -2 drop view error
- * @return -3 create view error
- */
- function setView($viewname, $definition,$comment) {
- return $this->createView($viewname, $definition, true, $comment);
- }
-
- /**
- * Rename a view
- * @param $view The current view's name
- * @param $name The new view's name
- * @return -1 Failed
- * @return 0 success
- */
- function renameView($view, $name) {
- $this->fieldClean($name);
- $this->fieldClean($view);
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$view}\" RENAME TO \"{$name}\"";
- if ($this->execute($sql) != 0)
- return -1;
- return 0;
- }
-
-
// Sequence functions
/**
- * Resets a given sequence to min value of sequence
- * @param $sequence Sequence name
- * @return 0 success
- * @return -1 sequence not found
- */
- function resetSequence($sequence) {
- // Get the minimum value of the sequence
- $seq = $this->getSequence($sequence);
- if ($seq->recordCount() != 1) return -1;
- $minvalue = $seq->fields['min_value'];
-
- /* This double-cleaning is deliberate */
- $this->fieldClean($sequence);
- $this->clean($sequence);
-
- $sql = "SELECT pg_catalog.SETVAL('\"{$this->_schema}\".\"{$sequence}\"', {$minvalue})";
-
- return $this->execute($sql);
- }
-
- /**
- * Execute nextval on a given sequence
- * @param $sequence Sequence name
- * @return 0 success
- * @return -1 sequence not found
- */
- function nextvalSequence($sequence) {
- /* This double-cleaning is deliberate */
- $this->fieldClean($sequence);
- $this->clean($sequence);
-
- $sql = "SELECT pg_catalog.NEXTVAL('\"{$this->_schema}\".\"{$sequence}\"')";
-
- return $this->execute($sql);
- }
-
- /**
- * Execute setval on a given sequence
- * @param $sequence Sequence name
- * @param $nextvalue The next value
- * @return 0 success
- * @return -1 sequence not found
- */
- function setvalSequence($sequence, $nextvalue) {
- /* This double-cleaning is deliberate */
- $this->fieldClean($sequence);
- $this->clean($sequence);
- $this->clean($nextvalue);
-
- $sql = "SELECT pg_catalog.SETVAL('\"{$this->_schema}\".\"{$sequence}\"', '{$nextvalue}')";
-
- return $this->execute($sql);
- }
-
- /**
- * Returns all sequences in the current database
- * @return A recordset
- */
- function getSequences($all = false) {
- if ($all) {
- // Exclude pg_catalog and information_schema tables
- $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
- FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
- WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
- AND c.relkind = 'S'
- AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
- ORDER BY nspname, seqname";
- } else {
- $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment
- FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
- WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
- AND c.relkind = 'S' AND n.nspname='{$this->_schema}' ORDER BY seqname";
- }
-
- return $this->selectSet( $sql );
- }
-
- /**
- * Returns properties of a single sequence
- * @param $sequence Sequence name
- * @return A recordset
- */
- function getSequence($sequence) {
- $this->fieldClean($sequence);
-
- $sql = "SELECT c.relname AS seqname, s.*,
- pg_catalog.obj_description(s.tableoid, 'pg_class') AS seqcomment,
- u.usename AS seqowner, n.nspname
- FROM \"{$sequence}\" AS s, pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
- WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
- AND c.relname = '{$sequence}' AND c.relkind = 'S' AND n.nspname='{$this->_schema}'
- AND n.oid = c.relnamespace";
-
- return $this->selectSet( $sql );
- }
-
- /**
- * Rename a sequence
- * @param $sequence The sequence name
- * @param $name The new name for the sequence
- * @return 0 success
- */
- function renameSequence($sequence, $name) {
- $this->fieldClean($name);
- $this->fieldClean($sequence);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$sequence}\" RENAME TO \"{$name}\"";
- return $this->execute($sql);
- }
-
- /**
- * Protected method which alter a sequence
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $seqrs The sequence recordSet returned by getSequence()
- * @param $name The new name for the sequence
- * @param $comment The comment on the sequence
- * @param $owner The new owner for the sequence
- * @param $schema The new schema for the sequence
- * @param $increment The increment
- * @param $minvalue The min value
- * @param $maxvalue The max value
- * @param $startvalue The starting value
- * @param $cachevalue The cache value
- * @param $cycledvalue True if cycled, false otherwise
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -7 schema error
- */
- /*protected*/
- function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
-
- $sequence = $seqrs->fields['seqname'];
- $this->fieldClean($name);
- $this->clean($comment);
- $this->fieldClean($owner);
+ * Protected method which alter a sequence
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $seqrs The sequence recordSet returned by getSequence()
+ * @param $name The new name for the sequence
+ * @param $comment The comment on the sequence
+ * @param $owner The new owner for the sequence
+ * @param $schema The new schema for the sequence
+ * @param $increment The increment
+ * @param $minvalue The min value
+ * @param $maxvalue The max value
+ * @param $startvalue The starting value
+ * @param $cachevalue The cache value
+ * @param $cycledvalue True if cycled, false otherwise
+ * @return 0 success
+ * @return -3 rename error
+ * @return -4 comment error
+ * @return -5 owner error
+ */
+ protected
+ function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
+ $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
+ /* $schema not supported in pg80- */
+ $this->fieldArrayClean($seqrs->fields);
// Comment
- $status = $this->setComment('SEQUENCE', $sequence, '', $comment);
+ $this->clean($comment);
+ $status = $this->setComment('SEQUENCE', $seqrs->fields['seqname'], '', $comment);
if ($status != 0)
return -4;
- // Rename (only if name has changed)
- if ($name != $sequence) {
- $status = $this->renameSequence($sequence, $name);
- if ($status != 0)
- return -3;
- $sequence = $name;
- }
-
// Owner
- if (!empty($owner)) {
-
- // If owner has been changed, then do the alteration. We are
- // careful to avoid this generally as changing owner is a
- // superuser only function.
- if ($seqrs->fields['seqowner'] != $owner) {
- $sql = "ALTER TABLE \"{$sequence}\" OWNER TO \"{$owner}\"";
- $status = $this->execute($sql);
- if ($status != 0)
- return -5;
- }
- }
+ $this->fieldClean($owner);
+ $status = $this->alterSequenceOwner($seqrs, $owner);
+ if ($status != 0)
+ return -5;
+
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterSequenceName($seqrs, $name);
+ if ($status != 0)
+ return -3;
return 0;
}
- /**
- * Drops a given sequence
- * @param $sequence Sequence name
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropSequence($sequence, $cascade) {
- $this->fieldClean($sequence);
-
- $sql = "DROP SEQUENCE \"{$this->_schema}\".\"{$sequence}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
+ // Indexe functions
/**
* Grabs a list of indexes for a table
@@ -1171,573 +231,93 @@ class Postgres73 extends Postgres72 {
return $this->selectSet($sql);
}
- /**
- * Removes an index from the database
- * @param $index The index to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropIndex($index, $cascade) {
- $this->fieldClean($index);
-
- $sql = "DROP INDEX \"{$this->_schema}\".\"{$index}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
+ // Role, User and Group functions
/**
- * Clusters an index
- * @param $index The name of the index
- * @param $table The table the index is on
- * @return 0 success
+ * Returns users in a specific group
+ * @param $groname The name of the group
+ * @return All users in the group
*/
- function clusterIndex($index, $table) {
- $this->fieldClean($index);
- $this->fieldClean($table);
+ function getGroup($groname) {
+ $this->clean($groname);
- // We don't bother with a transaction here, as there's no point rolling
- // back an expensive cluster if a cheap analyze fails for whatever reason
- $sql = "CLUSTER \"{$index}\" ON \"{$this->_schema}\".\"{$table}\"";
+ $sql = "SELECT grolist FROM pg_group WHERE groname = '{$groname}'";
- return $this->execute($sql);
- }
+ $grodata = $this->selectSet($sql);
+ if ($grodata->fields['grolist'] !== null && $grodata->fields['grolist'] != '{}') {
+ $members = $grodata->fields['grolist'];
+ $members = ereg_replace("\{|\}","",$members);
+ $this->clean($members);
- /**
- * Rebuild indexes
- * @param $type 'DATABASE' or 'TABLE' or 'INDEX'
- * @param $name The name of the specific database, table, or index to be reindexed
- * @param $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in 7.2-7.3, ignored in >=7.4
- */
- function reindex($type, $name, $force = false) {
- $this->fieldClean($name);
- switch($type) {
- case 'DATABASE':
- $sql = "REINDEX {$type} \"{$name}\"";
- if ($force) $sql .= ' FORCE';
- break;
- case 'TABLE':
- case 'INDEX':
- $sql = "REINDEX {$type} \"{$this->_schema}\".\"{$name}\"";
- if ($force) $sql .= ' FORCE';
- break;
- default:
- return -1;
- }
-
- return $this->execute($sql);
+ $sql = "SELECT usename FROM pg_user WHERE usesysid IN ({$members}) ORDER BY usename";
}
-
- /**
- * Grabs a single trigger
- * @param $table The name of a table whose triggers to retrieve
- * @param $trigger The name of the trigger to retrieve
- * @return A recordset
- */
- function getTrigger($table, $trigger) {
- $this->clean($table);
- $this->clean($trigger);
-
- $sql = "SELECT * FROM pg_catalog.pg_trigger t, pg_catalog.pg_class c
- WHERE t.tgrelid=c.oid
- AND c.relname='{$table}'
- AND t.tgname='{$trigger}'
- AND c.relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
+ else $sql = "SELECT usename FROM pg_user WHERE false";
return $this->selectSet($sql);
}
- /**
- * Grabs a list of triggers on a table
- * @param $table The name of a table whose triggers to retrieve
- * @return A recordset
- */
- function getTriggers($table = '') {
- $this->clean($table);
-
- $sql = "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
- t.tgargs, t.tgnargs, t.tgconstrrelid,
- (SELECT relname FROM pg_catalog.pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
- p.proname AS tgfname, c.relname, NULL AS tgdef,
- p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
- ns.nspname AS pronamespace
- FROM pg_catalog.pg_namespace ns,
- pg_catalog.pg_trigger t LEFT JOIN pg_catalog.pg_proc p
- ON t.tgfoid=p.oid, pg_catalog.pg_class c
- WHERE t.tgrelid=c.oid
- AND c.relname='{$table}'
- AND c.relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
- AND (NOT tgisconstraint OR NOT EXISTS
- (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
- AND p.pronamespace = ns.oid";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a trigger
- * @param $tgname The name of the trigger to create
- * @param $table The name of the table
- * @param $tgproc The function to execute
- * @param $tgtime BEFORE or AFTER
- * @param $tgevent Event
- * @param $tgargs The function arguments
- * @return 0 success
- */
- function createTrigger($tgname, $table, $tgproc, $tgtime, $tgevent, $tgfrequency, $tgargs) {
- $this->fieldClean($tgname);
- $this->fieldClean($table);
- $this->fieldClean($tgproc);
-
- /* No Statement Level Triggers in PostgreSQL (by now) */
- $sql = "CREATE TRIGGER \"{$tgname}\" {$tgtime}
- {$tgevent} ON \"{$this->_schema}\".\"{$table}\"
- FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops a trigger
- * @param $tgname The name of the trigger to drop
- * @param $table The table from which to drop the trigger
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropTrigger($tgname, $table, $cascade) {
- $this->fieldClean($tgname);
- $this->fieldClean($table);
-
- $sql = "DROP TRIGGER \"{$tgname}\" ON \"{$this->_schema}\".\"{$table}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Alters a trigger
- * @param $table The name of the table containing the trigger
- * @param $trigger The name of the trigger to alter
- * @param $name The new name for the trigger
- * @return 0 success
- */
- function alterTrigger($table, $trigger, $name) {
- $this->fieldClean($table);
- $this->fieldClean($trigger);
- $this->fieldClean($name);
-
- $sql = "ALTER TRIGGER \"{$trigger}\" ON \"{$this->_schema}\".\"{$table}\" RENAME TO \"{$name}\"";
-
- return $this->execute($sql);
- }
-
// Function functions
/**
- * Returns a list of all functions in the database
- * @param $all If true, will find all available functions, if false just those in search path
- * @param $type If not null, will find all functions with return value = type
- *
- * @return All functions
- */
- function getFunctions($all = false, $type = null) {
- if ($all) {
- $where = 'pg_catalog.pg_function_is_visible(p.oid)';
- $distinct = 'DISTINCT ON (p.proname)';
-
- if ($type) {
- $where .= " AND p.prorettype = (select oid from pg_catalog.pg_type p where p.typname = 'trigger') ";
- }
- }
- else {
- $where = "n.nspname = '{$this->_schema}'";
-
- $distinct = '';
- }
-
- $sql = "SELECT
- {$distinct}
- p.oid AS prooid,
- p.proname,
- p.proretset,
- pg_catalog.format_type(p.prorettype, NULL) AS proresult,
- pg_catalog.oidvectortypes(p.proargtypes) AS proarguments,
- pl.lanname AS prolanguage,
- pg_catalog.obj_description(p.oid, 'pg_proc') AS procomment,
- p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
- CASE WHEN p.proretset THEN 'setof ' ELSE '' END || pg_catalog.format_type(p.prorettype, NULL) AS proreturns,
- u.usename AS proowner
- FROM pg_catalog.pg_proc p
- INNER JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
- INNER JOIN pg_catalog.pg_language pl ON pl.oid = p.prolang
- LEFT JOIN pg_catalog.pg_user u ON u.usesysid = p.proowner
- WHERE NOT p.proisagg
- AND {$where}
- ORDER BY p.proname, proresult
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all details for a particular function
- * @param $func The name of the function to retrieve
- * @return Function info
- */
- function getFunction($function_oid) {
- $this->clean($function_oid);
-
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- pg_catalog.pg_get_userbyid(proowner) AS proowner,
- nspname as proschema,
- lanname as prolanguage,
- pg_catalog.format_type(prorettype, NULL) as proresult,
- prosrc,
- probin,
- proretset,
- proisstrict,
- provolatile,
- prosecdef,
- pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
- pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment
- FROM
- pg_catalog.pg_proc pc, pg_catalog.pg_language pl, pg_catalog.pg_namespace n
- WHERE
- pc.oid = '$function_oid'::oid
- AND pc.prolang = pl.oid
- AND n.oid = pc.pronamespace
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns an array containing a function's properties
- * @param $f The array of data for the function
- * @return An array containing the properties
- */
- function getFunctionProperties($f) {
- $temp = array();
-
- // Volatility
- if ($f['provolatile'] == 'v')
- $temp[] = 'VOLATILE';
- elseif ($f['provolatile'] == 'i')
- $temp[] = 'IMMUTABLE';
- elseif ($f['provolatile'] == 's')
- $temp[] = 'STABLE';
- else
- return -1;
-
- // Null handling
- $f['proisstrict'] = $this->phpBool($f['proisstrict']);
- if ($f['proisstrict'])
- $temp[] = 'RETURNS NULL ON NULL INPUT';
- else
- $temp[] = 'CALLED ON NULL INPUT';
-
- // Security
- $f['prosecdef'] = $this->phpBool($f['prosecdef']);
- if ($f['prosecdef'])
- $temp[] = 'SECURITY DEFINER';
- else
- $temp[] = 'SECURITY INVOKER';
-
- return $temp;
- }
-
- /**
- * Returns a list of all functions that can be used in triggers
- */
- function getTriggerFunctions() {
- return $this->getFunctions(true, 'trigger');
- }
-
- /**
- * Creates a new function.
+ * Updates (replaces) a function.
+ * @param $function_oid The OID of the function
* @param $funcname The name of the function to create
- * @param $args A comma separated string of types
+ * @param $newname The new name for the function
+ * @param $args The array of argument types
* @param $returns The return type
* @param $definition The definition for the new function
* @param $language The language the function is written for
* @param $flags An array of optional flags
- * @param $setof True if it returns a set, false otherwise
- * @param $replace (optional) True if OR REPLACE, false for normal
- * @return 0 success
- */
- function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $replace = false) {
- $this->fieldClean($funcname);
- $this->clean($args);
- $this->clean($language);
- $this->arrayClean($flags);
-
- $sql = "CREATE";
- if ($replace) $sql .= " OR REPLACE";
- $sql .= " FUNCTION \"{$this->_schema}\".\"{$funcname}\" (";
-
- if ($args != '')
- $sql .= $args;
-
- // For some reason, the returns field cannot have quotes...
- $sql .= ") RETURNS ";
- if ($setof) $sql .= "SETOF ";
- $sql .= "{$returns} AS ";
-
- if (is_array($definition)) {
- $this->arrayClean($definition);
- $sql .= "'" . $definition[0] . "'";
- if ($definition[1]) {
- $sql .= ",'" . $definition[1] . "'";
- }
- } else {
- $this->clean($definition);
- $sql .= "'" . $definition . "'";
- }
-
- $sql .= " LANGUAGE \"{$language}\"";
-
- // Add flags
- foreach ($flags as $v) {
- // Skip default flags
- if ($v == '') continue;
- else $sql .= "\n{$v}";
- }
-
- return $this->execute($sql);
- }
-
- /**
- * Drops a function.
- * @param $function_oid The OID of the function to drop
- * @param $cascade True to cascade drop, false to restrict
+ * @param $setof True if returns a set, false otherwise
+ * @param $comment The comment on the function
* @return 0 success
+ * @return -1 transaction error
+ * @return -2 drop function error
+ * @return -3 create function error
+ * @return -4 comment error
*/
- function dropFunction($function_oid, $cascade) {
- // Function comes in with $object as function OID
- $fn = $this->getFunction($function_oid);
- $this->fieldClean($fn->fields['proname']);
-
- $sql = "DROP FUNCTION \"{$this->_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- // Type functions
-
- /**
- * Returns a list of all types in the database
- * @param $all If true, will find all available functions, if false just those in search path
- * @param $tabletypes If true, will include table types
- * @param $domains If true, will include domains
- * @return A recordet
- */
- function getTypes($all = false, $tabletypes = false, $domains = false) {
- if ($all)
- $where = '1 = 1';
- else
- $where = "n.nspname = '{$this->_schema}'";
- // Never show system table types
- $where2 = "AND c.relnamespace NOT IN (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname LIKE 'pg@_%' ESCAPE '@')";
-
- // Create type filter
- $tqry = "'c'";
- if ($tabletypes)
- $tqry .= ", 'r', 'v'";
-
- // Create domain filter
- if (!$domains)
- $where .= " AND t.typtype != 'd'";
-
- $sql = "SELECT
- t.typname AS basename,
- pg_catalog.format_type(t.oid, NULL) AS typname,
- pu.usename AS typowner,
- t.typtype,
- pg_catalog.obj_description(t.oid, 'pg_type') AS typcomment
- FROM (pg_catalog.pg_type t
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace)
- LEFT JOIN pg_catalog.pg_user pu ON t.typowner = pu.usesysid
- WHERE (t.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid {$where2}))
- AND t.typname !~ '^_'
- AND {$where}
- ORDER BY typname
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Drops a type.
- * @param $typname The name of the type to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropType($typname, $cascade) {
- $this->fieldClean($typname);
-
- $sql = "DROP TYPE \"{$this->_schema}\".\"{$typname}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Creates a new composite type in the database
- * @param $name The name of the type
- * @param $fields The number of fields
- * @param $field An array of field names
- * @param $type An array of field types
- * @param $array An array of '' or '[]' for each type if it's an array or not
- * @param $length An array of field lengths
- * @param $colcomment An array of comments
- * @param $typcomment Type comment
- * @return 0 success
- * @return -1 no fields supplied
- */
- function createCompositeType($name, $fields, $field, $type, $array, $length, $colcomment, $typcomment) {
- $this->fieldClean($name);
- $this->clean($typcomment);
-
+ function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $rows, $cost, $comment) {
+ // Begin a transaction
$status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $found = false;
- $first = true;
- $comment_sql = ''; // Accumulate comments for the columns
- $sql = "CREATE TYPE \"{$this->_schema}\".\"{$name}\" AS (";
- for ($i = 0; $i < $fields; $i++) {
- $this->fieldClean($field[$i]);
- $this->clean($type[$i]);
- $this->clean($length[$i]);
- $this->clean($colcomment[$i]);
-
- // Skip blank columns - for user convenience
- if ($field[$i] == '' || $type[$i] == '') continue;
- // If not the first column, add a comma
- if (!$first) $sql .= ", ";
- else $first = false;
-
- switch ($type[$i]) {
- // Have to account for weird placing of length for with/without
- // time zone types
- case 'timestamp with time zone':
- case 'timestamp without time zone':
- $qual = substr($type[$i], 9);
- $sql .= "\"{$field[$i]}\" timestamp";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
- $sql .= $qual;
- break;
- case 'time with time zone':
- case 'time without time zone':
- $qual = substr($type[$i], 4);
- $sql .= "\"{$field[$i]}\" time";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
- $sql .= $qual;
- break;
- default:
- $sql .= "\"{$field[$i]}\" {$type[$i]}";
- if ($length[$i] != '') $sql .= "({$length[$i]})";
- }
- // Add array qualifier if necessary
- if ($array[$i] == '[]') $sql .= '[]';
-
- if ($colcomment[$i] != '') $comment_sql .= "COMMENT ON COLUMN \"{$this->_schema}\".\"{$name}\".\"{$field[$i]}\" IS '{$colcomment[$i]}';\n";
-
- $found = true;
- }
-
- if (!$found) return -1;
-
- $sql .= ")";
-
- $status = $this->execute($sql);
- if ($status) {
+ if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
- if ($typcomment != '') {
- $status = $this->setComment('TYPE', $name, '', $typcomment, true);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
+ // Replace the existing function
+ if ($funcname != $newname) {
+ $status = $this->dropFunction($function_oid, false);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
}
- if ($comment_sql != '') {
- $status = $this->execute($comment_sql);
- if ($status) {
+ $status = $this->createFunction($newname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, false);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+ } else {
+ $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, true);
+ if ($status != 0) {
$this->rollbackTransaction();
- return -1;
- }
+ return -3;
}
- return $this->endTransaction();
-
- }
-
- // Rule functions
-
- /**
- * Removes a rule from a table OR view
- * @param $rule The rule to drop
- * @param $relation The relation from which to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropRule($rule, $relation, $cascade) {
- $this->fieldClean($rule);
- $this->fieldClean($relation);
-
- $sql = "DROP RULE \"{$rule}\" ON \"{$this->_schema}\".\"{$relation}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
}
- /**
- * Returns a list of all rules on a table OR view
- * @param $table The table to find rules for
- * @return A recordset
- */
- function getRules($table) {
- $this->clean($table);
-
- $sql = "SELECT
- *
- FROM
- pg_catalog.pg_rules
- WHERE
- schemaname='{$this->_schema}'
- AND tablename='{$table}'
- ORDER BY
- rulename
- ";
-
- return $this->selectSet($sql);
- }
+ // Comment on the function
+ $this->fieldClean($newname);
+ $this->clean($comment);
+ $status = $this->setComment('FUNCTION', "\"{$newname}\"({$args})", null, $comment);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -4;
+ }
- /**
- * Edits a rule on a table OR view
- * @param $name The name of the new rule
- * @param $event SELECT, INSERT, UPDATE or DELETE
- * @param $table Table on which to create the rule
- * @param $where When to execute the rule, '' indicates always
- * @param $instead True if an INSTEAD rule, false otherwise
- * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
- * @param $action The action to take
- * @return 0 success
- * @return -1 invalid event
- */
- function setRule($name, $event, $table, $where, $instead, $type, $action) {
- return $this->createRule($name, $event, $table, $where, $instead, $type, $action, true);
+ return $this->endTransaction();
}
- // Constraint functions
+ // Constraint functions
/**
* Returns a list of all constraints on a table
@@ -1752,7 +332,7 @@ class Postgres73 extends Postgres72 {
* primary key constraints. */
$sql = "
SELECT conname, consrc, contype, indkey, indisclustered FROM (
- SELECT
+ SELECT
conname,
CASE WHEN contype='f' THEN
pg_catalog.pg_get_constraintdef(oid)
@@ -1763,12 +343,12 @@ class Postgres73 extends Postgres72 {
conrelid AS relid,
NULL AS indkey,
FALSE AS indisclustered
- FROM
+ FROM
pg_catalog.pg_constraint
- WHERE
+ WHERE
contype IN ('f', 'c')
UNION ALL
- SELECT
+ SELECT
pc.relname,
NULL,
CASE WHEN indisprimary THEN
@@ -1779,10 +359,10 @@ class Postgres73 extends Postgres72 {
pi.indrelid,
indkey,
pi.indisclustered
- FROM
+ FROM
pg_catalog.pg_class pc,
pg_catalog.pg_index pi
- WHERE
+ WHERE
pc.oid=pi.indexrelid
AND EXISTS (
SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
@@ -1800,13 +380,13 @@ class Postgres73 extends Postgres72 {
return $this->selectSet($sql);
}
- /**
+ /**
* Returns a list of all constraints on a table,
* including constraint name, definition, related col and referenced namespace,
* table and col if needed
* @param $table the table where we are looking for fk
* @return a recordset
- */
+ */
function getConstraintsWithFields($table) {
global $data;
@@ -1816,9 +396,9 @@ class Postgres73 extends Postgres72 {
$sql = "SELECT DISTINCT
max(SUBSTRING(array_dims(c.conkey) FROM '^\\\[.*:(.*)\\\]$')) as nb
FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
- JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
+ pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
+ JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
WHERE
r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
@@ -1830,9 +410,9 @@ class Postgres73 extends Postgres72 {
// get the max number of col used in a constraint for the table
$sql = "SELECT i.indkey
FROM
- pg_catalog.pg_index AS i
- JOIN pg_catalog.pg_class AS r ON (i.indrelid = r.oid)
- JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
+ pg_catalog.pg_index AS i
+ JOIN pg_catalog.pg_class AS r ON (i.indrelid = r.oid)
+ JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
WHERE
r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
@@ -1844,18 +424,18 @@ class Postgres73 extends Postgres72 {
$tmp = count(explode(' ', $rs->fields['indkey']));
$max_col_ind = $tmp > $max_col_ind ? $tmp : $max_col_ind;
$rs->MoveNext();
- }
+ }
$sql = "
SELECT contype, conname, consrc, ns1.nspname as p_schema, sub.relname as p_table,
- f_schema, f_table, p_field, f_field, indkey
+ f_schema, f_table, p_field, f_field, indkey
FROM (
- SELECT
+ SELECT
contype, conname,
CASE WHEN contype='f' THEN
- pg_catalog.pg_get_constraintdef(c.oid)
+ pg_catalog.pg_get_constraintdef(c.oid)
ELSE
- 'CHECK (' || consrc || ')'
+ 'CHECK (' || consrc || ')'
END AS consrc, r1.relname,
f1.attname as p_field, ns2.nspname as f_schema, r2.relname as f_table,
conrelid, r1.relnamespace, f2.attname as f_field, NULL AS indkey
@@ -1865,774 +445,97 @@ class Postgres73 extends Postgres72 {
JOIN pg_catalog.pg_attribute AS f1 ON ((f1.attrelid=c.conrelid) AND (f1.attnum=c.conkey[1]";
for ($i = 2; $i <= $max_col_cstr; $i++) {
$sql.= " OR f1.attnum=c.conkey[$i]";
- }
+ }
$sql .= "))
LEFT JOIN (
- pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
+ pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
) ON (c.confrelid=r2.oid)
LEFT JOIN pg_catalog.pg_attribute AS f2 ON
- ((f2.attrelid=r2.oid) AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)";
+ ((f2.attrelid=r2.oid) AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)";
for ($i = 2; $i <= $max_col_cstr; $i++)
$sql.= "OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
$sql .= "))
- WHERE
- contype IN ('f', 'c')
- UNION ALL
- SELECT
- CASE WHEN indisprimary THEN
- 'p'
- ELSE
- 'u'
- END as contype,
- pc.relname as conname, NULL as consrc, r2.relname, f1.attname as p_field,
- NULL as f_schema, NULL as f_table, indrelid as conrelid, pc.relnamespace,
- NULL as f_field, indkey
- FROM
- pg_catalog.pg_class pc, pg_catalog.pg_index pi
- JOIN pg_catalog.pg_attribute AS f1 ON ((f1.attrelid=pi.indrelid) AND (f1.attnum=pi.indkey[0]";
+ WHERE
+ contype IN ('f', 'c')
+ UNION ALL
+ SELECT
+ CASE WHEN indisprimary THEN
+ 'p'
+ ELSE
+ 'u'
+ END as contype,
+ pc.relname as conname, NULL as consrc, r2.relname, f1.attname as p_field,
+ NULL as f_schema, NULL as f_table, indrelid as conrelid, pc.relnamespace,
+ NULL as f_field, indkey
+ FROM
+ pg_catalog.pg_class pc, pg_catalog.pg_index pi
+ JOIN pg_catalog.pg_attribute AS f1 ON ((f1.attrelid=pi.indrelid) AND (f1.attnum=pi.indkey[0]";
for ($i = 1; $i <= $max_col_ind; $i++) {
$sql.= " OR f1.attnum=pi.indkey[$i]";
- }
+ }
$sql .= "))
- JOIN pg_catalog.pg_class r2 ON (pi.indrelid=r2.oid)
- WHERE
- pc.oid=pi.indexrelid
- AND EXISTS (
- SELECT 1 FROM pg_catalog.pg_depend AS d JOIN pg_catalog.pg_constraint AS c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = pc.tableoid AND d.objid = pc.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
- )
+ JOIN pg_catalog.pg_class r2 ON (pi.indrelid=r2.oid)
+ WHERE
+ pc.oid=pi.indexrelid
+ AND EXISTS (
+ SELECT 1
+ FROM pg_catalog.pg_depend AS d
+ JOIN pg_catalog.pg_constraint AS c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = pc.tableoid AND d.objid = pc.oid
+ AND d.deptype = 'i' AND c.contype IN ('u', 'p')
+ )
) AS sub
- JOIN pg_catalog.pg_namespace AS ns1 ON sub.relnamespace=ns1.oid
+ JOIN pg_catalog.pg_namespace AS ns1 ON sub.relnamespace=ns1.oid
WHERE conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname='{$this->_schema}'))
+ AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
+ WHERE nspname='{$this->_schema}'))
ORDER BY 1
";
return $this->selectSet($sql);
}
- /**
- * A function for getting all columns linked by foreign keys given a group of tables
- * @param $tables multi dimensional assoc array that holds schema and table name
- * @return A recordset of linked tables and columns
- * @return -1 $tables isn't an array
- */
- function getLinkingKeys($tables) {
- if (!is_array($tables)) return -1;
-
-
- $tables_list = "'{$tables[0]['tablename']}'";
- $schema_list = "'{$tables[0]['schemaname']}'";
- $schema_tables_list = "'{$tables[0]['schemaname']}.{$tables[0]['tablename']}'";
- for ($i = 1; $i < sizeof($tables); $i++) {
- $tables_list .= ", '{$tables[$i]['tablename']}'";
- $schema_list .= ", '{$tables[$i]['schemaname']}'";
- $schema_tables_list .= ", '{$tables[$i]['schemaname']}.{$tables[$i]['tablename']}'";
- }
- $maxDimension = 1;
-
- $sql = "
- SELECT DISTINCT
- array_dims(pc.conkey) AS arr_dim,
- pgc1.relname AS p_table
- FROM
- pg_catalog.pg_constraint AS pc,
- pg_catalog.pg_class AS pgc1
- WHERE
- pc.contype = 'f'
- AND (pc.conrelid = pgc1.relfilenode OR pc.confrelid = pgc1.relfilenode)
- AND pgc1.relname IN ($tables_list)
- ";
-
- //parse our output to find the highest dimension of foreign keys since pc.conkey is stored in an array
- $rs = $this->selectSet($sql);
- while (!$rs->EOF) {
- $arrData = explode(':', $rs->fields['arr_dim']);
- $tmpDimension = intval(substr($arrData[1], 0, strlen($arrData[1] - 1)));
- $maxDimension = $tmpDimension > $maxDimension ? $tmpDimension : $maxDimension;
- $rs->MoveNext();
- }
-
- //we know the highest index for foreign keys that conkey goes up to, expand for us in an IN query
- $cons_str = '( (pfield.attnum = conkey[1] AND cfield.attnum = confkey[1]) ';
- for ($i = 2; $i <= $maxDimension; $i++) {
- $cons_str .= "OR (pfield.attnum = conkey[{$i}] AND cfield.attnum = confkey[{$i}]) ";
- }
- $cons_str .= ') ';
-
- $sql = "
- SELECT
- pgc1.relname AS p_table,
- pgc2.relname AS f_table,
- pfield.attname AS p_field,
- cfield.attname AS f_field,
- pgns1.nspname AS p_schema,
- pgns2.nspname AS f_schema
- FROM
- pg_catalog.pg_constraint AS pc,
- pg_catalog.pg_class AS pgc1,
- pg_catalog.pg_class AS pgc2,
- pg_catalog.pg_attribute AS pfield,
- pg_catalog.pg_attribute AS cfield,
- (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns1,
- (SELECT oid AS ns_id, nspname FROM pg_catalog.pg_namespace WHERE nspname IN ($schema_list) ) AS pgns2
- WHERE
- pc.contype = 'f'
- AND pgc1.relnamespace = pgns1.ns_id
- AND pgc2.relnamespace = pgns2.ns_id
- AND pc.conrelid = pgc1.relfilenode
- AND pc.confrelid = pgc2.relfilenode
- AND pfield.attrelid = pc.conrelid
- AND cfield.attrelid = pc.confrelid
- AND $cons_str
- AND pgns1.nspname || '.' || pgc1.relname IN ($schema_tables_list)
- AND pgns2.nspname || '.' || pgc2.relname IN ($schema_tables_list)
- ";
- return $this->selectSet($sql);
- }
+ // Misc functions
/**
- * Removes a constraint from a relation
- * @param $constraint The constraint to drop
- * @param $relation The relation from which to drop
- * @param $type The type of constraint (c, f, u or p)
- * @param $cascade True to cascade drop, false to restrict
+ * Sets up the data object for a dump. eg. Starts the appropriate
+ * transaction, sets variables, etc.
* @return 0 success
*/
- function dropConstraint($constraint, $relation, $type, $cascade) {
- $this->fieldClean($constraint);
- $this->fieldClean($relation);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Finds the foreign keys that refer to the specified table
- * @param $table The table to find referrers for
- * @return A recordset
- */
- function getReferrers($table) {
- $this->clean($table);
-
+ function beginDump() {
+ // Begin serializable transaction (to dump consistent data)
$status = $this->beginTransaction();
if ($status != 0) return -1;
- $sql = "
- SELECT
- pn.nspname,
- pl.relname,
- pc.conname,
- pg_catalog.pg_get_constraintdef(pc.oid) AS consrc
- FROM
- pg_catalog.pg_constraint pc,
- pg_catalog.pg_namespace pn,
- pg_catalog.pg_class pl
- WHERE
- pc.connamespace = pn.oid
- AND pc.conrelid = pl.oid
- AND pc.contype = 'f'
- AND confrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname='{$this->_schema}'))
- ORDER BY 1,2,3
- ";
-
- return $this->selectSet($sql);
- }
-
- // Privilege functions
-
- /**
- * Grabs an array of users and their privileges for an object,
- * given its type.
- * @param $object The name of the object whose privileges are to be retrieved
- * @param $type The type of the object (eg. database, schema, relation, function or language)
- * @return Privileges array
- * @return -1 invalid type
- * @return -2 object not found
- * @return -3 unknown privilege type
- */
- function getPrivileges($object, $type) {
- $this->clean($object);
-
- switch ($type) {
- case 'table':
- case 'view':
- case 'sequence':
- $sql = "SELECT relacl AS acl FROM pg_catalog.pg_class WHERE relname='{$object}'
- AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')";
- break;
- case 'database':
- $sql = "SELECT datacl AS acl FROM pg_catalog.pg_database WHERE datname='{$object}'";
- break;
- case 'function':
- // Since we fetch functions by oid, they are already constrained to
- // the current schema.
- $sql = "SELECT proacl AS acl FROM pg_catalog.pg_proc WHERE oid='{$object}'";
- break;
- case 'language':
- $sql = "SELECT lanacl AS acl FROM pg_catalog.pg_language WHERE lanname='{$object}'";
- break;
- case 'schema':
- $sql = "SELECT nspacl AS acl FROM pg_catalog.pg_namespace WHERE nspname='{$object}'";
- break;
- case 'tablespace':
- $sql = "SELECT spcacl AS acl FROM pg_catalog.pg_tablespace WHERE spcname='{$object}'";
- break;
- default:
- return -1;
- }
-
- // Fetch the ACL for object
- $acl = $this->selectField($sql, 'acl');
- if ($acl == -1) return -2;
- elseif ($acl == '' || $acl == null) return array();
- else return $this->_parseACL($acl);
- }
-
- // Domain functions
-
- /**
- * Gets all information for a single domain
- * @param $domain The name of the domain to fetch
- * @return A recordset
- */
- function getDomain($domain) {
- $this->clean($domain);
-
- $sql = "
- SELECT
- t.typname AS domname,
- pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
- t.typnotnull AS domnotnull,
- t.typdefault AS domdef,
- pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
- pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
- FROM
- pg_catalog.pg_type t
- WHERE
- t.typtype = 'd'
- AND t.typname = '{$domain}'
- AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = '{$this->_schema}')";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Return all domains in current schema. Excludes domain constraints.
- * @return All tables, sorted alphabetically
- */
- function getDomains() {
- $sql = "
- SELECT
- t.typname AS domname,
- pg_catalog.format_type(t.typbasetype, t.typtypmod) AS domtype,
- t.typnotnull AS domnotnull,
- t.typdefault AS domdef,
- pg_catalog.pg_get_userbyid(t.typowner) AS domowner,
- pg_catalog.obj_description(t.oid, 'pg_type') AS domcomment
- FROM
- pg_catalog.pg_type t
- WHERE
- t.typtype = 'd'
- AND t.typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname='{$this->_schema}')
- ORDER BY t.typname";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a domain
- * @param $domain The name of the domain to create
- * @param $type The base type for the domain
- * @param $length Optional type length
- * @param $array True for array type, false otherwise
- * @param $notnull True for NOT NULL, false otherwise
- * @param $default Default value for domain
- * @param $check A CHECK constraint if there is one
- * @return 0 success
- */
- function createDomain($domain, $type, $length, $array, $notnull, $default, $check) {
- $this->fieldClean($domain);
-
- $sql = "CREATE DOMAIN \"{$this->_schema}\".\"{$domain}\" AS ";
-
- if ($length == '')
- $sql .= $type;
- else {
- switch ($type) {
- // Have to account for weird placing of length for with/without
- // time zone types
- case 'timestamp with time zone':
- case 'timestamp without time zone':
- $qual = substr($type, 9);
- $sql .= "timestamp({$length}){$qual}";
- break;
- case 'time with time zone':
- case 'time without time zone':
- $qual = substr($type, 4);
- $sql .= "time({$length}){$qual}";
- break;
- default:
- $sql .= "{$type}({$length})";
- }
- }
-
- // Add array qualifier, if requested
- if ($array) $sql .= '[]';
-
- if ($notnull) $sql .= ' NOT NULL';
- if ($default != '') $sql .= " DEFAULT {$default}";
- if ($this->hasDomainConstraints() && $check != '') $sql .= " CHECK ({$check})";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops a domain.
- * @param $domain The name of the domain to drop
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropDomain($domain, $cascade) {
- $this->fieldClean($domain);
-
- $sql = "DROP DOMAIN \"{$this->_schema}\".\"{$domain}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
+ // Set serializable
+ $sql = "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
}
- // Find object functions
-
- /**
- * Searches all system catalogs to find objects that match a certain name.
- * @param $term The search term
- * @param $filter The object type to restrict to ('' means no restriction)
- * @return A recordset
- */
- function findObject($term, $filter) {
- global $conf;
-
- // Escape search term for ILIKE match
- $term = str_replace('_', '\\_', $term);
- $term = str_replace('%', '\\%', $term);
- $this->clean($term);
- $this->clean($filter);
-
- // Exclude system relations if necessary
- if (!$conf['show_system']) {
- // XXX: The mention of information_schema here is in the wrong place, but
- // it's the quickest fix to exclude the info schema from 7.4
- $where = " AND pn.nspname NOT LIKE 'pg\\\\_%' AND pn.nspname != 'information_schema'";
- $lan_where = "AND pl.lanispl";
- }
- else {
- $where = '';
- $lan_where = '';
- }
-
- // Apply outer filter
- $sql = '';
- if ($filter != '') {
- $sql = "SELECT * FROM (";
- }
-
- $sql .= "
- SELECT 'SCHEMA' AS type, oid, NULL AS schemaname, NULL AS relname, nspname AS name
- FROM pg_catalog.pg_namespace pn WHERE nspname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT CASE WHEN relkind='r' THEN 'TABLE' WHEN relkind='v' THEN 'VIEW' WHEN relkind='S' THEN 'SEQUENCE' END, pc.oid,
- pn.nspname, NULL, pc.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
- WHERE pc.relnamespace=pn.oid AND relkind IN ('r', 'v', 'S') AND relname ILIKE '%{$term}%' {$where}
- UNION ALL
- 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,
- pg_catalog.pg_attribute pa WHERE pc.relnamespace=pn.oid AND pc.oid=pa.attrelid
- AND pa.attname ILIKE '%{$term}%' AND pa.attnum > 0 AND NOT pa.attisdropped AND pc.relkind IN ('r', 'v') {$where}
- UNION ALL
- 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
- WHERE pp.pronamespace=pn.oid AND NOT pp.proisagg AND pp.proname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'INDEX', NULL, pn.nspname, pc.relname, pc2.relname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
- pg_catalog.pg_index pi, pg_catalog.pg_class pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pi.indrelid
- AND pi.indexrelid=pc2.oid
- AND NOT EXISTS (
- SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
- )
- AND pc2.relname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'CONSTRAINTTABLE', NULL, pn.nspname, pc.relname, pc2.conname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
- pg_catalog.pg_constraint pc2 WHERE pc.relnamespace=pn.oid AND pc.oid=pc2.conrelid AND pc2.conrelid != 0
- AND CASE WHEN pc2.contype IN ('f', 'c') THEN TRUE ELSE NOT EXISTS (
- SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = pc2.tableoid AND d.objid = pc2.oid AND d.deptype = 'i' AND c.contype IN ('u', 'p')
- ) END
- AND pc2.conname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'CONSTRAINTDOMAIN', pt.oid, pn.nspname, pt.typname, pc.conname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn,
- pg_catalog.pg_constraint pc WHERE pt.typnamespace=pn.oid AND pt.oid=pc.contypid AND pc.contypid != 0
- AND pc.conname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'TRIGGER', NULL, pn.nspname, pc.relname, pt.tgname FROM pg_catalog.pg_class pc, pg_catalog.pg_namespace pn,
- pg_catalog.pg_trigger pt WHERE pc.relnamespace=pn.oid AND pc.oid=pt.tgrelid
- AND (NOT pt.tgisconstraint OR NOT EXISTS
- (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = pt.tableoid AND d.objid = pt.oid AND d.deptype = 'i' AND c.contype = 'f'))
- AND pt.tgname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'RULETABLE', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
- JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
- LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
- WHERE c.relkind='r' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'RULEVIEW', NULL, pn.nspname AS schemaname, c.relname AS tablename, r.rulename FROM pg_catalog.pg_rewrite r
- JOIN pg_catalog.pg_class c ON c.oid = r.ev_class
- LEFT JOIN pg_catalog.pg_namespace pn ON pn.oid = c.relnamespace
- WHERE c.relkind='v' AND r.rulename != '_RETURN' AND r.rulename ILIKE '%{$term}%' {$where}
- ";
-
- // Add advanced objects if show_advanced is set
- if ($conf['show_advanced']) {
- $sql .= "
- UNION ALL
- SELECT CASE WHEN pt.typtype='d' THEN 'DOMAIN' ELSE 'TYPE' END, pt.oid, pn.nspname, NULL,
- pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
- WHERE pt.typnamespace=pn.oid AND typname ILIKE '%{$term}%'
- AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
- {$where}
- UNION ALL
- SELECT 'OPERATOR', po.oid, pn.nspname, NULL, po.oprname FROM pg_catalog.pg_operator po, pg_catalog.pg_namespace pn
- WHERE po.oprnamespace=pn.oid AND oprname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'CONVERSION', pc.oid, pn.nspname, NULL, pc.conname FROM pg_catalog.pg_conversion pc,
- pg_catalog.pg_namespace pn WHERE pc.connamespace=pn.oid AND conname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_catalog.pg_language pl
- WHERE lanname ILIKE '%{$term}%' {$lan_where}
- UNION ALL
- SELECT DISTINCT ON (p.proname) 'AGGREGATE', p.oid, pn.nspname, NULL, p.proname FROM pg_catalog.pg_proc p
- LEFT JOIN pg_catalog.pg_namespace pn ON p.pronamespace=pn.oid
- WHERE p.proisagg AND p.proname ILIKE '%{$term}%' {$where}
- UNION ALL
- SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, pn.nspname, NULL, po.opcname FROM pg_catalog.pg_opclass po,
- pg_catalog.pg_namespace pn WHERE po.opcnamespace=pn.oid
- AND po.opcname ILIKE '%{$term}%' {$where}
- ";
- }
- // Otherwise just add domains
- else {
- $sql .= "
- UNION ALL
- SELECT 'DOMAIN', pt.oid, pn.nspname, NULL,
- pt.typname FROM pg_catalog.pg_type pt, pg_catalog.pg_namespace pn
- WHERE pt.typnamespace=pn.oid AND pt.typtype='d' AND typname ILIKE '%{$term}%'
- AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = pt.typrelid))
- {$where}
- ";
- }
-
- if ($filter != '') {
- // We use like to make RULE, CONSTRAINT and COLUMN searches work
- $sql .= ") AS sub WHERE type LIKE '{$filter}%' ";
+ // Set datestyle to ISO
+ $sql = "SET DATESTYLE = ISO";
+ $status = $this->execute($sql);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
}
-
- $sql .= "ORDER BY type, schemaname, relname, name";
-
- return $this->selectSet($sql);
- }
-
- // Operator functions
-
- /**
- * Returns a list of all operators in the database
- * @return All operators
- */
- function getOperators() {
- // We stick with the subselects here, as you cannot ORDER BY a regtype
- $sql = "
- SELECT
- po.oid,
- po.oprname,
- (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
- (SELECT pg_catalog.format_type(oid, NULL) FROM pg_catalog.pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
- po.oprresult::pg_catalog.regtype AS resultname,
- pg_catalog.obj_description(po.oid, 'pg_operator') AS oprcomment
- FROM
- pg_catalog.pg_operator po
- WHERE
- po.oprnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}')
- ORDER BY
- po.oprname, oprleftname, oprrightname
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all details for a particular operator
- * @param $operator_oid The oid of the operator
- * @return Function info
- */
- function getOperator($operator_oid) {
- $this->clean($operator_oid);
-
- $sql = "
- SELECT
- po.oid,
- po.oprname,
- oprleft::pg_catalog.regtype AS oprleftname,
- oprright::pg_catalog.regtype AS oprrightname,
- oprresult::pg_catalog.regtype AS resultname,
- po.oprcanhash,
- oprcom::pg_catalog.regoperator AS oprcom,
- oprnegate::pg_catalog.regoperator AS oprnegate,
- oprlsortop::pg_catalog.regoperator AS oprlsortop,
- oprrsortop::pg_catalog.regoperator AS oprrsortop,
- oprltcmpop::pg_catalog.regoperator AS oprltcmpop,
- oprgtcmpop::pg_catalog.regoperator AS oprgtcmpop,
- po.oprcode::pg_catalog.regproc AS oprcode,
- po.oprrest::pg_catalog.regproc AS oprrest,
- po.oprjoin::pg_catalog.regproc AS oprjoin
- FROM
- pg_catalog.pg_operator po
- WHERE
- po.oid='{$operator_oid}'
- ";
-
- return $this->selectSet($sql);
- }
-
- // Cast functions
-
- /**
- * Returns a list of all casts in the database
- * @return All casts
- */
- function getCasts() {
- global $conf;
-
- if ($conf['show_system'])
- $where = '';
- else
- $where = "
- AND n1.nspname NOT LIKE 'pg\\\\_%'
- AND n2.nspname NOT LIKE 'pg\\\\_%'
- AND n3.nspname NOT LIKE 'pg\\\\_%'
- ";
-
- $sql = "
- SELECT
- c.castsource::pg_catalog.regtype AS castsource,
- c.casttarget::pg_catalog.regtype AS casttarget,
- CASE WHEN c.castfunc=0 THEN NULL
- ELSE c.castfunc::pg_catalog.regprocedure END AS castfunc,
- c.castcontext,
- obj_description(c.oid, 'pg_cast') as castcomment
- FROM
- (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),
- pg_catalog.pg_type t1,
- pg_catalog.pg_type t2,
- pg_catalog.pg_namespace n1,
- pg_catalog.pg_namespace n2
- WHERE
- c.castsource=t1.oid
- AND c.casttarget=t2.oid
- AND t1.typnamespace=n1.oid
- AND t2.typnamespace=n2.oid
- {$where}
- ORDER BY 1, 2
- ";
-
- return $this->selectSet($sql);
- }
-
- // Conversion functions
-
- /**
- * Returns a list of all conversions in the database
- * @return All conversions
- */
- function getConversions() {
- $sql = "
- SELECT
- c.conname,
- pg_catalog.pg_encoding_to_char(c.conforencoding) AS conforencoding,
- pg_catalog.pg_encoding_to_char(c.contoencoding) AS contoencoding,
- c.condefault,
- pg_catalog.obj_description(c.oid, 'pg_conversion') AS concomment
- FROM pg_catalog.pg_conversion c, pg_catalog.pg_namespace n
- WHERE n.oid = c.connamespace
- AND n.nspname='{$this->_schema}'
- ORDER BY 1;
- ";
-
- return $this->selectSet($sql);
- }
-
- // Language functions
-
- /**
- * Gets all languages
- * @param $all True to get all languages, regardless of show_system
- * @return A recordset
- */
- function getLanguages($all = false) {
- global $conf;
-
- if ($conf['show_system'] || $all)
- $where = '';
- else
- $where = 'WHERE lanispl';
-
- $sql = "
- SELECT
- lanname,
- lanpltrusted,
- lanplcallfoid::pg_catalog.regproc AS lanplcallf
- FROM
- pg_catalog.pg_language
- {$where}
- ORDER BY
- lanname
- ";
-
- return $this->selectSet($sql);
- }
-
- // Aggregate functions
-
- /**
- * Gets all information for an aggregate
- * @param $name The name of the aggregate
- * @param $basetype The input data type of the aggregate
- * @return A recordset
- */
- function getAggregate($name, $basetype) {
- $this->fieldclean($name);
- $this->fieldclean($basetype);
-
- $sql = "
- SELECT p.proname,
- CASE p.proargtypes[0]
- WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL
- ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
- END AS proargtypes, a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype,
- a.aggfinalfn, a.agginitval, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
- FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
- WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
- AND p.proisagg AND n.nspname='{$this->_schema}'
- AND p.proname='" . $name . "'
- AND CASE p.proargtypes[0]
- WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN ''
- ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
- END ='" . $basetype . "'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Gets all aggregates
- * @return A recordset
- */
- function getAggregates() {
- $sql = "SELECT p.proname, CASE p.proargtypes[0] WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL ELSE
- pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes, a.aggtransfn, u.usename,
- pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
- FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
- WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
- AND p.proisagg AND n.nspname='{$this->_schema}' ORDER BY 1, 2";
-
- return $this->selectSet($sql);
- }
-
- // Operator Class functions
-
- /**
- * Gets all opclasses
- * @return A recordset
- */
- function getOpClasses() {
- $sql = "
- SELECT
- pa.amname,
- po.opcname,
- po.opcintype::pg_catalog.regtype AS opcintype,
- po.opcdefault,
- pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
- FROM
- pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
- WHERE
- po.opcamid=pa.oid
- AND po.opcnamespace=pn.oid
- AND pn.nspname='{$this->_schema}'
- ORDER BY 1,2
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Removes an aggregate function from the database
- * @param $aggrname The name of the aggregate
- * @param $aggrtype The input data type of the aggregate
- * @param $cascade True to cascade drop, false to restrict
- * @return 0 success
- */
- function dropAggregate($aggrname, $aggrtype, $cascade) {
- $this->fieldClean($aggrname);
- $this->fieldClean($aggrtype);
-
- $sql = "DROP AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\")";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- // Query functions
-
- /**
- * Returns explained version of a query
- * @param $query The query for which to get data
- * @param $analyze True to analyze as well
- */
- function getExplainSQL($query, $analyze) {
- $temp = "EXPLAIN ";
- if ($analyze) $temp .= "ANALYZE ";
- $temp .= $query;
- return $temp;
}
// Capabilities
- function hasSchemas() { return true; }
- function hasConversions() { return true; }
- function hasIsClustered() { return true; }
- function hasDropBehavior() { return true; }
- function hasDropColumn() { return true; }
- function hasDomains() { return true; }
- function hasAlterTrigger() { return true; }
- function hasCasts() { return true; }
- function hasPrepare() { return true; }
- function hasUserSessionDefaults() { return true; }
- function hasVariables() { return true; }
-// function hasForeignKeysInfo() { return true; }
- function hasConstraintsInfo() { return true; }
- function hasViewColumnRename() { return true; }
- function hasUserAndDbVariables() { return true; }
- function hasCompositeTypes() { return true; }
- function hasFuncPrivs() { return true; }
- function hasLocksView() { return true; }
+ function hasAlterAggregate() { return false; }
+ function hasAlterDatabaseRename() { return false; }
+ function hasAlterSequenceProps() { return false; }
+ function hasCreateTableLike() {return false;}
+ function hasDomainConstraints() { return false; }
+ function hasGrantOption() { return false; }
+ function hasReadOnlyQueries() { return false; }
+ function hasRecluster() { return false; }
+ function hasUserRename() { return false; }
}
?>
diff --git a/classes/database/Postgres74.php b/classes/database/Postgres74.php
index 1502c21c..3fe7739f 100644
--- a/classes/database/Postgres74.php
+++ b/classes/database/Postgres74.php
@@ -7,27 +7,30 @@
* $Id: Postgres74.php,v 1.72 2008/02/20 21:06:18 ioguix Exp $
*/
-include_once('./classes/database/Postgres73.php');
+include_once('./classes/database/Postgres80.php');
-class Postgres74 extends Postgres73 {
+class Postgres74 extends Postgres80 {
var $major_version = 7.4;
+ // List of all legal privileges that can be applied to different types
+ // of objects.
+ var $privlist = array(
+ 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
+ 'database' => array('CREATE', 'TEMPORARY', 'ALL PRIVILEGES'),
+ 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
+ 'language' => array('USAGE', 'ALL PRIVILEGES'),
+ 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES')
+ );
- // Last oid assigned to a system object
- var $_lastSystemOID = 17137;
-
- // Max object name length
- var $_maxNameLen = 63;
-
- // How often to execute the trigger
- var $triggerFrequency = array('ROW','STATEMENT');
/**
* Constructor
* @param $conn The database connection
*/
function Postgres74($conn) {
- $this->Postgres73($conn);
+ $this->Postgres80($conn);
}
// Help functions
@@ -50,8 +53,7 @@ class Postgres74 extends Postgres73 {
* @return -2 owner error
* @return -3 rename error
*/
- function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
- {
+ function alterDatabase($dbName, $newName, $newOwner = '', $comment = '') {
//ignore $newowner, not supported pre 8.0
//ignore $comment, not supported pre 8.2
$this->clean($dbName);
@@ -63,479 +65,143 @@ class Postgres74 extends Postgres73 {
}
/**
- * Renames a database, note that this operation cannot be
- * performed on a database that is currently being connected to
- * @param string $oldName name of database to rename
- * @param string $newName new name of database
- * @return int 0 on success
- */
- function alterDatabaseRename($oldName, $newName) {
- $this->clean($oldName);
- $this->clean($newName);
-
- if ($oldName != $newName) {
- $sql = "ALTER DATABASE \"{$oldName}\" RENAME TO \"{$newName}\"";
- return $this->execute($sql);
- }
- else //just return success, we're not going to do anything
- return 0;
- }
-
- // Table functions
-
- /**
- * Get the fields for uniquely identifying a row in a table
- * @param $table The table for which to retrieve the identifier
- * @return An array mapping attribute number to attribute name, empty for no identifiers
- * @return -1 error
- */
- function getRowIdentifier($table) {
- $oldtable = $table;
- $this->clean($table);
-
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- // Get the first primary or unique index (sorting primary keys first) that
- // is NOT a partial index.
- $sql = "SELECT indrelid, indkey FROM pg_catalog.pg_index WHERE indisunique AND
- indrelid=(SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}' AND
- relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
- AND indpred IS NULL AND indexprs IS NULL ORDER BY indisprimary DESC LIMIT 1";
- $rs = $this->selectSet($sql);
-
- // If none, check for an OID column. Even though OIDs can be duplicated, the edit and delete row
- // functions check that they're only modiying a single row. Otherwise, return empty array.
- if ($rs->recordCount() == 0) {
- // Check for OID column
- $temp = array();
- if ($this->hasObjectID($table)) {
- $temp = array('oid');
- }
- $this->endTransaction();
- return $temp;
- }
- // Otherwise find the names of the keys
- else {
- $attnames = $this->getAttributeNames($oldtable, explode(' ', $rs->fields['indkey']));
- if (!is_array($attnames)) {
- $this->rollbackTransaction();
- return -1;
- }
- else {
- $this->endTransaction();
- return $attnames;
- }
- }
- }
-
- /**
- * Sets up the data object for a dump. eg. Starts the appropriate
- * transaction, sets variables, etc.
- * @return 0 success
- */
- function beginDump() {
- $status = parent::beginDump();
- if ($status != 0) return $status;
-
- // Set extra_float_digits to 2
- $sql = "SET extra_float_digits TO 2";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- // Constraint functions
-
- /**
- * Returns a list of all constraints on a table
- * @param $table The table to find rules for
- * @return A recordset
- */
- function getConstraints($table) {
- $this->clean($table);
-
- // This SQL is greatly complicated by the need to retrieve
- // index clustering information for primary and unique constraints
- $sql = "SELECT
- pc.conname,
- pg_catalog.pg_get_constraintdef(pc.oid, true) AS consrc,
- pc.contype,
- CASE WHEN pc.contype='u' OR pc.contype='p' THEN (
- SELECT
- indisclustered
- FROM
- pg_catalog.pg_depend pd,
- pg_catalog.pg_class pl,
- pg_catalog.pg_index pi
- WHERE
- pd.refclassid=pc.tableoid
- AND pd.refobjid=pc.oid
- AND pd.objid=pl.oid
- AND pl.oid=pi.indexrelid
- ) ELSE
- NULL
- END AS indisclustered
- FROM
- pg_catalog.pg_constraint pc
- WHERE
- pc.conrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname='{$this->_schema}'))
- ORDER BY
- 1
- ";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns a list of all constraints on a table,
- * including constraint name, definition, related col and referenced namespace,
- * table and col if needed
- * @param $table the table where we are looking for fk
- * @return a recordset
- */
- function getConstraintsWithFields($table) {
- global $data;
-
- $data->clean($table);
-
- // get the max number of col used in a constraint for the table
- $sql = "SELECT DISTINCT
- max(SUBSTRING(array_dims(c.conkey) FROM '^\\\[.*:(.*)\\\]$')) as nb
- FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
- JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
- WHERE
- r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
-
- $rs = $this->selectSet($sql);
-
- if ($rs->EOF) $max_col = 0;
- else $max_col = $rs->fields['nb'];
-
- $sql = '
- SELECT
- c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc,
- ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
- r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
- pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
- FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
- JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
- for ($i = 2; $i <= $rs->fields['nb']; $i++) {
- $sql.= " OR f1.attnum=c.conkey[$i]";
- }
- $sql.= '))
- JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
- LEFT JOIN (
- pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
- ) ON (c.confrelid=r2.oid)
- LEFT JOIN pg_catalog.pg_attribute AS f2 ON
- (f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
- for ($i = 2; $i <= $rs->fields['nb']; $i++)
- $sql.= "OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
-
- $sql .= sprintf("))
- WHERE
- r1.relname = '%s' AND ns1.nspname='%s'
- ORDER BY 1", $table, $this->_schema);
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a new table in the database copying attribs and other properties from another table
- * @param $name The name of the table
- * @param $like an array giving the schema ans the name of the table from which attribs are copying from:
- * array(
- * 'table' => table name,
- * 'schema' => the schema name,
- * )
- * @param $defaults if true, copy the defaults values as well
- * @param $constraints if true, copy the constraints as well (CHECK on table & attr)
- * @param $tablespace The tablespace name ('' means none/default)
- */
- function createTableLike($name, $like, $defaults = false, $constraints = false, $idx = false, $tablespace = '') {
- $this->fieldClean($name);
-
- $this->fieldClean($like['schema']);
- $this->fieldClean($like['table']);
- $like = "\"{$like['schema']}\".\"{$like['table']}\"";
-
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $sql = "CREATE TABLE \"{$this->_schema}\".\"{$name}\" (LIKE {$like}";
-
- if ($defaults) $sql .= " INCLUDING DEFAULTS";
- if ($this->hasCreateTableLikeWithConstraints() && $constraints) $sql .= " INCLUDING CONSTRAINTS";
- if ($this->hasCreateTableLikeWithIndexes() && $idx) $sql .= " INCLUDING INDEXES";
-
- $sql .= ")";
-
- if ($this->hasTablespaces() && $tablespace != '') {
- $this->fieldClean($tablespace);
- $sql .= " TABLESPACE \"{$tablespace}\"";
- }
-
- $status = $this->execute($sql);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
-
- return $this->endTransaction();
- }
-
- // Group functions
-
- /**
- * Return users in a specific group
- * @param $groname The name of the group
- * @return All users in the group
+ * Return all database available on the server
+ * @return A list of databases, sorted alphabetically
*/
- function getGroup($groname) {
- $this->clean($groname);
+ function getDatabases($currentdatabase = NULL) {
+ global $conf, $misc;
- $sql = "SELECT s.usename FROM pg_catalog.pg_user s, pg_catalog.pg_group g
- WHERE g.groname='{$groname}' AND s.usesysid = ANY (g.grolist)
- ORDER BY s.usename";
+ $server_info = $misc->getServerInfo();
- return $this->selectSet($sql);
+ if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
+ $username = $server_info['username'];
+ $this->clean($username);
+ $clause = " AND pu.usename='{$username}'";
}
+ else $clause = '';
- // Schema functions
-
- /**
- * Return all schemas in the current database. This differs from the version
- * in 7.3 only in that it considers the information_schema to be a system schema.
- * @return All schemas, sorted alphabetically
- */
- function getSchemas() {
- global $conf, $slony;
-
- if (!$conf['show_system']) {
- $where = "WHERE nspname NOT LIKE 'pg@_%' ESCAPE '@' AND nspname != 'information_schema'";
- if (isset($slony) && $slony->isEnabled()) {
- $temp = $slony->slony_schema;
- $this->clean($temp);
- $where .= " AND nspname != '{$temp}'";
- }
-
- }
- else $where = "WHERE nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
- $sql = "SELECT pn.nspname, pu.usename AS nspowner, pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
- FROM pg_catalog.pg_namespace pn LEFT JOIN pg_catalog.pg_user pu ON (pn.nspowner = pu.usesysid)
- {$where} ORDER BY nspname";
-
- return $this->selectSet($sql);
- }
-
- // Index functions
-
- /**
- * Grabs a list of indexes for a table
- * @param $table The name of a table whose indexes to retrieve
- * @param $unique Only get unique/pk indexes
- * @return A recordset
- */
- function getIndexes($table = '', $unique = false) {
- $this->clean($table);
-
- $sql = "SELECT c2.relname AS indname, i.indisprimary, i.indisunique, i.indisclustered,
- pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS inddef
- FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
- WHERE c.relname = '{$table}' AND pg_catalog.pg_table_is_visible(c.oid)
- AND c.oid = i.indrelid AND i.indexrelid = c2.oid
- ";
- if ($unique) $sql .= " AND i.indisunique ";
- $sql .= " ORDER BY c2.relname";
-
- return $this->selectSet($sql);
- }
-
- // View functions
-
- /**
- * Returns all details for a particular view
- * @param $view The name of the view to retrieve
- * @return View info
- */
- function getView($view) {
- $this->clean($view);
-
- $sql = "SELECT c.relname, n.nspname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
- pg_catalog.pg_get_viewdef(c.oid, true) AS vwdefinition, pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
- FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.relnamespace)
- WHERE (c.relname = '$view')
- AND n.nspname='{$this->_schema}'";
-
- return $this->selectSet($sql);
- }
-
- // Trigger functions
-
- /**
- * Grabs a list of triggers on a table
- * @param $table The name of a table whose triggers to retrieve
- * @return A recordset
- */
- function getTriggers($table = '') {
- $this->clean($table);
-
- $sql = "SELECT
- t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef, t.tgenabled, p.oid AS prooid,
- p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
- ns.nspname AS pronamespace
- FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
- WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
- AND (NOT tgisconstraint OR NOT EXISTS
- (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
- AND p.oid=t.tgfoid
- AND p.pronamespace = ns.oid";
-
- return $this->selectSet($sql);
- }
-
- // Administration functions
-
- /**
- * Recluster a table or all the tables in the current database
- * @param $table (optional) The table to recluster
- */
- function recluster($table = '') {
- if ($table != '') {
- $this->fieldClean($table);
- $sql = "CLUSTER \"{$this->_schema}\".\"{$table}\"";
- }
+ if ($currentdatabase != NULL)
+ $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
else
- $sql = "CLUSTER";
+ $orderby = "ORDER BY pdb.datname";
- return $this->execute($sql);
- }
-
- // Domain functions
-
- /**
- * Get domain constraints
- * @param $domain The name of the domain whose constraints to fetch
- * @return A recordset
- */
- function getDomainConstraints($domain) {
- $this->clean($domain);
+ if (!$conf['show_system'])
+ $where = ' AND NOT pdb.datistemplate';
+ else
+ $where = ' AND pdb.datallowconn';
- $sql = "
- SELECT
- conname,
- contype,
- pg_catalog.pg_get_constraintdef(oid, true) AS consrc
- FROM
- pg_catalog.pg_constraint
- WHERE
- contypid = (SELECT oid FROM pg_catalog.pg_type
- WHERE typname='{$domain}'
- AND typnamespace = (SELECT oid FROM pg_catalog.pg_namespace
- WHERE nspname = '{$this->_schema}'))
- ORDER BY
- conname";
+ $sql = "SELECT pdb.datname AS datname, pu.usename AS datowner, pg_encoding_to_char(encoding) AS datencoding,
+ (SELECT description FROM pg_description pd WHERE pdb.oid=pd.objoid) AS datcomment
+ FROM pg_database pdb, pg_user pu
+ WHERE pdb.datdba = pu.usesysid
+ {$where}
+ {$clause}
+ {$orderby}";
return $this->selectSet($sql);
}
+ // Table functions
+
/**
- * Drops a domain constraint
- * @param $domain The domain from which to remove the constraint
- * @param $constraint The constraint to remove
- * @param $cascade True to cascade, false otherwise
+ * Protected method which alter a table
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $tblrs The table recordSet returned by getTable()
+ * @param $name The new name for the table
+ * @param $owner The new owner for the table
+ * @param $schema The new schema for the table
+ * @param $comment The comment on the table
+ * @param $tablespace The new tablespace for the table ('' means leave as is)
* @return 0 success
+ * @return -3 rename error
+ * @return -4 comment error
+ * @return -5 owner error
*/
- function dropDomainConstraint($domain, $constraint, $cascade) {
- $this->fieldClean($domain);
- $this->fieldClean($constraint);
+ protected
+ function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP CONSTRAINT \"{$constraint}\"";
- if ($cascade) $sql .= " CASCADE";
+ /* $schema and tablespace not supported in pg74- */
+ $this->fieldArrayClean($tblrs->fields);
- return $this->execute($sql);
- }
+ // Comment
+ $this->clean($comment);
+ $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
+ if ($status != 0) return -4;
- /**
- * Adds a check constraint to a domain
- * @param $domain The domain to which to add the check
- * @param $definition The definition of the check
- * @param $name (optional) The name to give the check, otherwise default name is assigned
- * @return 0 success
- */
- function addDomainCheckConstraint($domain, $definition, $name = '') {
- $this->fieldClean($domain);
- $this->fieldClean($name);
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterTableOwner($tblrs, $owner);
+ if ($status != 0) return -5;
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" ADD ";
- if ($name != '') $sql .= "CONSTRAINT \"{$name}\" ";
- $sql .= "CHECK ({$definition})";
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterTableName($tblrs, $name);
+ if ($status != 0) return -3;
- return $this->execute($sql);
+ return 0;
}
/**
- * Alters a domain
- * @param $domain The domain to alter
- * @param $domdefault The domain default
- * @param $domnotnull True for NOT NULL, false otherwise
- * @param $domowner The domain owner
+ * Alters a column in a table OR view
+ * @param $table The table in which the column resides
+ * @param $column The column to alter
+ * @param $name The new name for the column
+ * @param $notnull (boolean) True if not null, false otherwise
+ * @param $oldnotnull (boolean) True if column is already not null, false otherwise
+ * @param $default The new default for the column
+ * @param $olddefault The old default for the column
+ * @param $type The new type for the column
+ * @param $array True if array type, false otherwise
+ * @param $length The optional size of the column (ie. 30 for varchar(30))
+ * @param $oldtype The old type for the column
+ * @param $comment Comment for the column
* @return 0 success
- * @return -1 transaction error
- * @return -2 default error
- * @return -3 not null error
- * @return -4 owner error
+ * @return -1 set not null error
+ * @return -2 set default error
+ * @return -3 rename column error
+ * @return -4 comment error
*/
- function alterDomain($domain, $domdefault, $domnotnull, $domowner) {
- $this->fieldClean($domain);
- $this->fieldClean($domowner);
+ function alterColumn($table, $column, $name, $notnull, $oldnotnull, $default, $olddefault,
+ $type, $length, $array, $oldtype, $comment) {
+ $this->beginTransaction();
- $status = $this->beginTransaction();
+ // @@ NEED TO HANDLE "NESTED" TRANSACTION HERE
+ if ($notnull != $oldnotnull) {
+ $status = $this->setColumnNull($table, $column, !$notnull);
if ($status != 0) {
$this->rollbackTransaction();
return -1;
}
+ }
- // Default
- if ($domdefault == '')
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP DEFAULT";
+ // Set default, if it has changed
+ if ($default != $olddefault) {
+ if ($default == '')
+ $status = $this->dropColumnDefault($table, $column);
else
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" SET DEFAULT {$domdefault}";
+ $status = $this->setColumnDefault($table, $column, $default);
- $status = $this->execute($sql);
if ($status != 0) {
$this->rollbackTransaction();
return -2;
}
+ }
- // NOT NULL
- if ($domnotnull)
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" SET NOT NULL";
- else
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" DROP NOT NULL";
-
- $status = $this->execute($sql);
+ // Rename the column, if it has been changed
+ if ($column != $name) {
+ $status = $this->renameColumn($table, $column, $name);
if ($status != 0) {
$this->rollbackTransaction();
return -3;
}
+ }
- // Owner
- $sql = "ALTER DOMAIN \"{$this->_schema}\".\"{$domain}\" OWNER TO \"{$domowner}\"";
-
- $status = $this->execute($sql);
+ // Parameters must be cleaned for the setComment function. It's ok to do
+ // that here since this is the last time these variables are used.
+ $this->fieldClean($name);
+ $this->fieldClean($table);
+ $this->clean($comment);
+ $status = $this->setComment('COLUMN', $name, $table, $comment);
if ($status != 0) {
$this->rollbackTransaction();
return -4;
@@ -544,326 +210,133 @@ class Postgres74 extends Postgres73 {
return $this->endTransaction();
}
- // User functions
-
/**
- * Renames a user
- * @param $username The username of the user to rename
- * @param $newname The new name of the user
- * @return 0 success
+ * Returns table information
+ * @param $table The name of the table
+ * @return A recordset
*/
- function renameUser($username, $newname){
- $this->fieldClean($username);
- $this->fieldClean($newname);
+ function getTable($table) {
+ $this->clean($table);
- $sql = "ALTER USER \"{$username}\" RENAME TO \"{$newname}\"";
+ $sql = "
+ SELECT
+ c.relname, n.nspname, u.usename AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind = 'r'
+ AND n.nspname = '{$this->_schema}'
+ AND c.relname = '{$table}'";
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
/**
- * Adjusts a user's info and renames the user
- * @param $username The username of the user to modify
- * @param $password A new password for the user
- * @param $createdb boolean Whether or not the user can create databases
- * @param $createuser boolean Whether or not the user can create other users
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire.
- * @param $newname The new name of the user
- * @return 0 success
- * @return -1 transaction error
- * @return -2 set user attributes error
- * @return -3 rename error
+ * Return all tables in current database (and schema)
+ * @param $all True to fetch all tables, false for just in current schema
+ * @return All tables, sorted alphabetically
*/
- function setRenameUser($username, $password, $createdb, $createuser, $expiry, $newname) {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $status = $this->setUser($username, $password, $createdb, $createuser, $expiry);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
-
- if ($username != $newname){
- $status = $this->renameUser($username, $newname);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
+ function getTables($all = false) {
+ if ($all) {
+ // Exclude pg_catalog and information_schema tables
+ $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
+ FROM pg_catalog.pg_tables
+ WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
+ ORDER BY schemaname, tablename";
+ } else {
+ $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
+ pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
+ reltuples::bigint
+ FROM pg_catalog.pg_class c
+ LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE c.relkind = 'r'
+ AND nspname='{$this->_schema}'
+ ORDER BY c.relname";
}
- return $this->endTransaction();
- }
-
- // Function functions
-
- /**
- * Updates (replaces) a function.
- * @param $function_oid The OID of the function
- * @param $funcname The name of the function to create
- * @param $newname The new name for the function
- * @param $args The array of argument types
- * @param $returns The return type
- * @param $definition The definition for the new function
- * @param $language The language the function is written for
- * @param $flags An array of optional flags
- * @param $setof True if returns a set, false otherwise
- * @param $comment The comment on the function
- * @return 0 success
- * @return -1 transaction error
- * @return -3 create function error
- * @return -4 comment error
- * @return -5 rename function error
- * @return -6 alter owner error
- * @return -7 alter schema error
- */
- function setFunction($function_oid, $funcname, $newname, $args, $returns, $definition, $language, $flags, $setof, $funcown, $newown, $funcschema, $newschema, $cost, $rows, $comment) {
- // Begin a transaction
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Replace the existing function
- $status = $this->createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, true);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
-
- // Comment on the function
- $this->fieldClean($funcname);
- $this->clean($comment);
- $status = $this->setComment('FUNCTION', "\"{$funcname}\"({$args})", null, $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- // Rename the function, if necessary
- $this->fieldClean($newname);
- if ($funcname != $newname) {
- $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) RENAME TO \"{$newname}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -5;
- }
-
- $funcname = $newname;
- }
-
- // Alter the owner, if necessary
- if ($this->hasFunctionAlterOwner()) {
- $this->fieldClean($newown);
- if ($funcown != $newown) {
- $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) OWNER TO \"{$newown}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -6;
- }
- }
-
- }
-
- // Alter the schema, if necessary
- if ($this->hasFunctionAlterSchema()) {
- $this->fieldClean($newschema);
- if ($funcschema != $newschema) {
- $sql = "ALTER FUNCTION \"{$this->_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -7;
- }
- }
- }
-
- return $this->endTransaction();
+ return $this->selectSet($sql);
}
- // Sequences
-
/**
- * Protected method which alter a sequence
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $seqrs The sequence recordSet returned by getSequence()
- * @param $name The new name for the sequence
- * @param $comment The comment on the sequence
- * @param $owner The new owner for the sequence
- * @param $schema The new schema for the sequence
- * @param $increment The increment
- * @param $minvalue The min value
- * @param $maxvalue The max value
- * @param $startvalue The starting value
- * @param $cachevalue The cache value
- * @param $cycledvalue True if cycled, false otherwise
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 get sequence error
+ * Returns the current default_with_oids setting
+ * @return default_with_oids setting
*/
- /*protected*/
- function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
-
- $status = parent::_alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue);
- if ($status != 0)
- return $status;
-
- /* $schema not supported in pg74 */
-
- // if name != seqname, sequence has been renamed in parent
- $sequence = ($seqrs->fields['seqname'] == $name) ? $seqrs->fields['seqname'] : $name;
- $this->clean($increment);
- $this->clean($minvalue);
- $this->clean($maxvalue);
- $this->clean($startvalue);
- $this->clean($cachevalue);
- $this->clean($cycledvalue);
-
- // Props
- $sql = '';
- if (!empty($increment) && ($increment != $seqrs->fields['increment_by'])) $sql .= " INCREMENT {$increment}";
- if (!empty($minvalue) && ($minvalue != $seqrs->fields['min_value'])) $sql .= " MINVALUE {$minvalue}";
- if (!empty($maxvalue) && ($maxvalue != $seqrs->fields['max_value'])) $sql .= " MAXVALUE {$maxvalue}";
- if (!empty($startvalue) && ($startvalue != $seqrs->fields['last_value'])) $sql .= " RESTART {$startvalue}";
- if (!empty($cachevalue) && ($cachevalue != $seqrs->fields['cache_value'])) $sql .= " CACHE {$cachevalue}";
- // toggle cycle yes/no
- if (!is_null($cycledvalue))
- $sql .= (!$cycledvalue ? ' NO ' : '') . " CYCLE";
- if ($sql != '') {
- $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$sequence}\" $sql";
- $status = $this->execute($sql);
- if ($status != 0)
- return -6;
+ function getDefaultWithOid() {
+ // 8.0 is the first release to have this setting
+ // Prior releases don't have this setting... oids always activated
+ return 'on';
}
- return 0;
- }
-
- // Aggregates
+ // Sequence functions
/**
- * Alters an aggregate
- * @param $aggrname The actual name of the aggregate
- * @param $aggrtype The actual input data type of the aggregate
- * @param $aggrowner The actual owner of the aggregate
- * @param $aggrschema The actual schema the aggregate belongs to
- * @param $aggrcomment The actual comment for the aggregate
- * @param $newaggrname The new name of the aggregate
- * @param $newaggrowner The new owner of the aggregate
- * @param $newaggrschema The new schema where the aggregate will belong to
- * @param $newaggrcomment The new comment for the aggregate
- * @return 0 success
- * @return -1 change owner error
- * @return -2 change comment error
- * @return -3 change schema error
- * @return -4 change name error
+ * Returns all sequences in the current database
+ * @return A recordset
*/
- function alterAggregate($aggrname, $aggrtype, $aggrowner, $aggrschema, $aggrcomment, $newaggrname, $newaggrowner, $newaggrschema, $newaggrcomment) {
- // Clean fields
- $this->fieldClean($aggrname);
- $this->fieldClean($aggrtype);
- $this->fieldClean($aggrowner);
- $this->fieldClean($aggrschema);
- $this->clean($aggrcomment);
- $this->fieldClean($newaggrname);
- $this->fieldClean($newaggrowner);
- $this->fieldClean($newaggrschema);
- $this->clean($newaggrcomment);
-
- $this->beginTransaction();
-
- // Change the owner, if it has changed
- if($aggrowner != $newaggrowner) {
- $status = $this->changeAggregateOwner($aggrname, $aggrtype, $newaggrowner);
- if($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
+ function getSequences($all = false) {
+ if ($all) {
+ // Exclude pg_catalog and information_schema tables
+ $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
+ FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
+ WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
+ AND c.relkind = 'S'
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
+ ORDER BY nspname, seqname";
+ } else {
+ $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment
+ FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
+ WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
+ AND c.relkind = 'S' AND n.nspname='{$this->_schema}' ORDER BY seqname";
}
- // Set the comment, if it has changed
- if($aggrcomment != $newaggrcomment) {
- $status = $this->setComment('AGGREGATE', $aggrname, '', $newaggrcomment, $aggrtype);
- if ($status) {
- $this->rollbackTransaction();
- return -2;
- }
+ return $this->selectSet( $sql );
}
- // Change the schema, if it has changed
- if($aggrschema != $newaggrschema) {
- $status = $this->changeAggregateSchema($aggrname, $aggrtype, $newaggrschema);
- if($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
-
- // Rename the aggregate, if it has changed
- if($aggrname != $newaggrname) {
- $status = $this->renameAggregate($newaggrschema, $aggrname, $aggrtype, $newaggrname);
- if($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
- }
-
- return $this->endTransaction();
- }
+ // Function functions
/**
- * Changes the owner of an aggregate function
- * @param $aggrname The name of the aggregate
- * @param $aggrtype The input data type of the aggregate
- * @param $newaggrowner The new owner of the aggregate
- * @return 0 success
+ * Returns all details for a particular function
+ * @param $func The name of the function to retrieve
+ * @return Function info
*/
- function changeAggregateOwner($aggrname, $aggrtype, $newaggrowner) {
- $sql = "ALTER AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") OWNER TO \"{$newaggrowner}\"";
- return $this->execute($sql);
- }
+ function getFunction($function_oid) {
+ $this->clean($function_oid);
- /**
- * Changes the schema of an aggregate function
- * @param $aggrname The name of the aggregate
- * @param $aggrtype The input data type of the aggregate
- * @param $newaggrschema The new schema for the aggregate
- * @return 0 success
- */
- function changeAggregateSchema($aggrname, $aggrtype, $newaggrschema) {
- $sql = "ALTER AGGREGATE \"{$this->_schema}\".\"{$aggrname}\" (\"{$aggrtype}\") SET SCHEMA \"{$newaggrschema}\"";
- return $this->execute($sql);
- }
+ $sql = "
+ SELECT
+ pc.oid AS prooid,
+ proname,
+ pg_catalog.pg_get_userbyid(proowner) AS proowner,
+ nspname as proschema,
+ lanname as prolanguage,
+ pg_catalog.format_type(prorettype, NULL) as proresult,
+ prosrc,
+ probin,
+ proretset,
+ proisstrict,
+ provolatile,
+ prosecdef,
+ pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
+ pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment
+ FROM
+ pg_catalog.pg_proc pc, pg_catalog.pg_language pl, pg_catalog.pg_namespace n
+ WHERE
+ pc.oid = '$function_oid'::oid
+ AND pc.prolang = pl.oid
+ AND n.oid = pc.pronamespace
+ ";
- /**
- * Renames an aggregate function
- * @param $aggrname The actual name of the aggregate
- * @param $aggrtype The actual input data type of the aggregate
- * @param $newaggrname The new name of the aggregate
- * @return 0 success
- */
- function renameAggregate($aggrschema, $aggrname, $aggrtype, $newaggrname) {
- $sql = "ALTER AGGREGATE \"{$aggrschema}\"" . '.' . "\"{$aggrname}\" (\"{$aggrtype}\") RENAME TO \"{$newaggrname}\"";
- return $this->execute($sql);
+ return $this->selectSet($sql);
}
// Capabilities
- function hasAlterDatabaseRename() { return true; }
- function hasGrantOption() { return true; }
- function hasDomainConstraints() { return true; }
- function hasUserRename() { return true; }
- function hasRecluster() { return true; }
- function hasReadOnlyQueries() { return true; }
- function hasAlterSequenceProps() { return true; }
- function hasAlterAggregate() { return true; }
- function hasCreateTableLike() { return true; }
-}
+ function hasAlterColumnType() { return false; }
+ function hasAlterDatabaseOwner() { return false; }
+ function hasFunctionAlterOwner() { return false; }
+ function hasNamedParams() { return false; }
+ function hasSignals() { return false; }
+ function hasTablespaces() { return false; }
+}
?>
diff --git a/classes/database/Postgres80.php b/classes/database/Postgres80.php
index 78472601..ebc0cac7 100644
--- a/classes/database/Postgres80.php
+++ b/classes/database/Postgres80.php
@@ -6,34 +6,52 @@
* $Id: Postgres80.php,v 1.28 2007/12/12 04:11:10 xzilla Exp $
*/
-include_once('./classes/database/Postgres74.php');
+include_once('./classes/database/Postgres81.php');
-class Postgres80 extends Postgres74 {
+class Postgres80 extends Postgres81 {
var $major_version = 8.0;
-
- // List of all legal privileges that can be applied to different types
- // of objects.
- var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
- 'database' => array('CREATE', 'TEMPORARY', 'ALL PRIVILEGES'),
- 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
- 'language' => array('USAGE', 'ALL PRIVILEGES'),
- 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES'),
- 'tablespace' => array('CREATE', 'ALL PRIVILEGES')
+ // Map of database encoding names to HTTP encoding names. If a
+ // database encoding does not appear in this list, then its HTTP
+ // encoding name is the same as its database encoding name.
+ var $codemap = array(
+ 'ALT' => 'CP866',
+ 'EUC_CN' => 'GB2312',
+ 'EUC_JP' => 'EUC-JP',
+ 'EUC_KR' => 'EUC-KR',
+ 'EUC_TW' => 'EUC-TW',
+ 'ISO_8859_5' => 'ISO-8859-5',
+ 'ISO_8859_6' => 'ISO-8859-6',
+ 'ISO_8859_7' => 'ISO-8859-7',
+ 'ISO_8859_8' => 'ISO-8859-8',
+ 'JOHAB' => 'CP1361',
+ 'KOI8' => 'KOI8-R',
+ 'LATIN1' => 'ISO-8859-1',
+ 'LATIN2' => 'ISO-8859-2',
+ 'LATIN3' => 'ISO-8859-3',
+ 'LATIN4' => 'ISO-8859-4',
+ // The following encoding map is a known error in PostgreSQL < 7.2
+ // See the constructor for Postgres72.
+ 'LATIN5' => 'ISO-8859-5',
+ 'LATIN6' => 'ISO-8859-10',
+ 'LATIN7' => 'ISO-8859-13',
+ 'LATIN8' => 'ISO-8859-14',
+ 'LATIN9' => 'ISO-8859-15',
+ 'LATIN10' => 'ISO-8859-16',
+ 'SQL_ASCII' => 'US-ASCII',
+ 'TCVN' => 'CP1258',
+ 'UNICODE' => 'UTF-8',
+ 'WIN' => 'CP1251',
+ 'WIN874' => 'CP874',
+ 'WIN1256' => 'CP1256'
);
- // Last oid assigned to a system object
- var $_lastSystemOID = 17228;
-
/**
* Constructor
* @param $conn The database connection
*/
function Postgres80($conn) {
- $this->Postgres74($conn);
+ $this->Postgres81($conn);
}
// Help functions
@@ -83,131 +101,9 @@ class Postgres80 extends Postgres74 {
return $this->selectSet($sql);
}
- /**
- * Alters a database
- * the multiple return vals are for postgres 8+ which support more functionality in alter database
- * @param $dbName The name of the database
- * @param $newName new name for the database
- * @param $newOwner The new owner for the database
- * @return 0 success
- * @return -1 transaction error
- * @return -2 owner error
- * @return -3 rename error
- */
- function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
- {
- $this->clean($dbName);
- $this->clean($newName);
- $this->clean($newOwner);
- //ignore $comment, not supported pre 8.2
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- if ($dbName != $newName) {
- $status = $this->alterDatabaseRename($dbName, $newName);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
-
- $status = $this->alterDatabaseOwner($newName, $newOwner);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
- return $this->endTransaction();
- }
-
- /**
- * Changes ownership of a database
- * This can only be done by a superuser or the owner of the database
- * @param string $dbName database to change ownership of
- * @param string $newOwner user that will own the database
- * @return int 0 on success
- */
- function alterDatabaseOwner($dbName, $newOwner) {
- $this->clean($dbName);
- $this->clean($newOwner);
-
- $sql = "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
- return $this->execute($sql);
- }
-
- /**
- * Returns the current default_with_oids setting
- * @return default_with_oids setting
- */
- function getDefaultWithOid() {
- // Try to avoid a query if at all possible (5)
- if (function_exists('pg_parameter_status')) {
- $default = pg_parameter_status($this->conn->_connectionID, 'default_with_oids');
- if ($default !== false) return $default;
- }
-
- $sql = "SHOW default_with_oids";
-
- return $this->selectField($sql, 'default_with_oids');
- }
-
// Table functions
/**
- * Return all tables in current database (and schema)
- * @param $all True to fetch all tables, false for just in current schema
- * @return All tables, sorted alphabetically
- */
- function getTables($all = false) {
- if ($all) {
- // Exclude pg_catalog and information_schema tables
- $sql = "SELECT schemaname AS nspname, tablename AS relname, tableowner AS relowner
- FROM pg_catalog.pg_tables
- WHERE schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
- ORDER BY schemaname, tablename";
- } else {
- $sql = "SELECT c.relname, pg_catalog.pg_get_userbyid(c.relowner) AS relowner,
- pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
- reltuples::bigint,
- (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind = 'r'
- AND nspname='{$this->_schema}'
- ORDER BY c.relname";
- }
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns table information
- * @param $table The name of the table
- * @return A recordset
- */
- function getTable($table) {
- $this->clean($table);
-
- $sql = "
- SELECT
- c.relname, n.nspname, u.usename AS relowner,
- pg_catalog.obj_description(c.oid, 'pg_class') AS relcomment,
- (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
- FROM pg_catalog.pg_class c
- LEFT JOIN pg_catalog.pg_user u ON u.usesysid = c.relowner
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
- WHERE c.relkind = 'r'
- AND n.nspname = '{$this->_schema}'
- AND n.oid = c.relnamespace
- AND c.relname = '{$table}'";
-
- return $this->selectSet($sql);
- }
-
- /**
* Protected method which alter a table
* SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
* @param $tblrs The table recordSet returned by getTable()
@@ -221,371 +117,194 @@ class Postgres80 extends Postgres74 {
* @return -4 comment error
* @return -5 owner error
* @return -6 tablespace error
- * @return -7 schema error
*/
- /* protected */
+ protected
function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
- $status = parent::_alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace);
- if ($status != 0)
- return $status;
+ /* $schema not supported in pg80- */
- // if name != tablename, table has been renamed in parent
- $tablename = ($tblrs->fields['relname'] == $name) ? $tblrs->fields['relname'] : $name;
+ // Comment
+ $this->clean($comment);
+ $status = $this->setComment('TABLE', '', $tblrs->fields['relname'], $comment);
+ if ($status != 0) return -4;
- /* $schema not supported in pg80 */
- $this->fieldClean($tablespace);
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterTableOwner($tblrs, $owner);
+ if ($status != 0) return -5;
// Tablespace
- if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
-
- // If tablespace has been changed, then do the alteration. We
- // don't want to do this unnecessarily.
- $sql = "ALTER TABLE \"{$tablename}\" SET TABLESPACE \"{$tablespace}\"";
-
- $status = $this->execute($sql);
+ $this->fieldClean($tablespace);
+ $status = $this->alterTableTablespace($tblrs, $tablespace);
if ($status != 0) return -6;
- }
+
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterTableName($tblrs, $name);
+ if ($status != 0) return -3;
return 0;
}
+ // View functions
+
/**
- * Alters a column in a table
- * @param $table The table in which the column resides
- * @param $column The column to alter
- * @param $name The new name for the column
- * @param $notnull (boolean) True if not null, false otherwise
- * @param $oldnotnull (boolean) True if column is already not null, false otherwise
- * @param $default The new default for the column
- * @param $olddefault The old default for the column
- * @param $type The new type for the column
- * @param $array True if array type, false otherwise
- * @param $length The optional size of the column (ie. 30 for varchar(30))
- * @param $oldtype The old type for the column
- * @param $comment Comment for the column
+ * Protected method which alter a view
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new name for the view
+ * @param $owner The new owner for the view
+ * @param $comment The comment on the view
* @return 0 success
- * @return -1 batch alteration failed
- * @return -3 rename column error
+ * @return -3 rename error
* @return -4 comment error
- * @return -6 transaction error
- */
- function alterColumn($table, $column, $name, $notnull, $oldnotnull, $default, $olddefault,
- $type, $length, $array, $oldtype, $comment) {
- $this->fieldClean($table);
- $this->fieldClean($column);
- $this->clean($comment);
-
- // Initialise an empty SQL string
- $sql = '';
-
- // Create the command for changing nullability
- if ($notnull != $oldnotnull) {
- $sql .= "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" " . (($notnull) ? 'SET' : 'DROP') . " NOT NULL";
- }
-
- // Add default, if it has changed
- if ($default != $olddefault) {
- if ($default == '') {
- if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
- else $sql .= ", ";
- $sql .= "ALTER COLUMN \"{$column}\" DROP DEFAULT";
- }
- else {
- if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
- else $sql .= ", ";
- $sql .= "ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";
- }
- }
-
- // Add type, if it has changed
- if ($length == '')
- $ftype = $type;
- else {
- switch ($type) {
- // Have to account for weird placing of length for with/without
- // time zone types
- case 'timestamp with time zone':
- case 'timestamp without time zone':
- $qual = substr($type, 9);
- $ftype = "timestamp({$length}){$qual}";
- break;
- case 'time with time zone':
- case 'time without time zone':
- $qual = substr($type, 4);
- $ftype = "time({$length}){$qual}";
- break;
- default:
- $ftype = "{$type}({$length})";
- }
- }
-
- // Add array qualifier, if requested
- if ($array) $ftype .= '[]';
-
- if ($ftype != $oldtype) {
- if ($sql == '') $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ";
- else $sql .= ", ";
- $sql .= "ALTER COLUMN \"{$column}\" TYPE {$ftype}";
- }
-
- // Begin transaction
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -6;
- }
-
- // Attempt to process the batch alteration, if anything has been changed
- if ($sql != '') {
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- // Update the comment on the column
- $status = $this->setComment('COLUMN', $column, $table, $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
-
- // Rename the column, if it has been changed
- if ($column != $name) {
- $status = $this->renameColumn($table, $column, $name);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
-
- return $this->endTransaction();
- }
-
- // Sequence functions
-
- /**
- * Returns all sequences in the current database
- * @return A recordset
- */
- function getSequences($all = false) {
- if ($all) {
- // Exclude pg_catalog and information_schema tables
- $sql = "SELECT n.nspname, c.relname AS seqname, u.usename AS seqowner
- FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
- WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
- AND c.relkind = 'S'
- AND n.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
- ORDER BY nspname, seqname";
- } else {
- $sql = "SELECT c.relname AS seqname, u.usename AS seqowner, pg_catalog.obj_description(c.oid, 'pg_class') AS seqcomment,
- (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=c.reltablespace) AS tablespace
- FROM pg_catalog.pg_class c, pg_catalog.pg_user u, pg_catalog.pg_namespace n
- WHERE c.relowner=u.usesysid AND c.relnamespace=n.oid
- AND c.relkind = 'S' AND n.nspname='{$this->_schema}' ORDER BY seqname";
- }
-
- return $this->selectSet( $sql );
- }
-
- // Tablespace functions
-
- /**
- * Retrieves information for all tablespaces
- * @param $all Include all tablespaces (necessary when moving objects back to the default space)
- * @return A recordset
- */
- function getTablespaces($all = false) {
- global $conf;
-
- $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation
- FROM pg_catalog.pg_tablespace";
-
- if (!$conf['show_system'] && !$all) {
- $sql .= " WHERE spcname NOT LIKE 'pg\\\\_%'";
- }
-
- $sql .= " ORDER BY spcname";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Retrieves a tablespace's information
- * @return A recordset
+ * @return -5 owner error
*/
- function getTablespace($spcname) {
- $this->clean($spcname);
+ protected
+ function _alterView($vwrs, $name, $owner, $schema, $comment) {
- $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation
- FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
+ /* $schema not supported in pg80- */
+ $this->fieldArrayClean($vwrs->fields);
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a tablespace
- * @param $spcname The name of the tablespace to create
- * @param $spcowner The owner of the tablespace. '' for current
- * @param $spcloc The directory in which to create the tablespace
- * @return 0 success
- */
- function createTablespace($spcname, $spcowner, $spcloc, $comment='') {
- $this->fieldClean($spcname);
- $this->clean($spcloc);
+ // Comment
$this->clean($comment);
+ if ($this->setComment('VIEW', $vwrs->fields['relname'], '', $comment) != 0)
+ return -4;
- $sql = "CREATE TABLESPACE \"{$spcname}\"";
-
- if ($spcowner != '') {
- $this->fieldClean($spcowner);
- $sql .= " OWNER \"{$spcowner}\"";
- }
-
- $sql .= " LOCATION '{$spcloc}'";
-
- $status = $this->execute($sql);
- if ($status != 0) return -1;
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterViewOwner($vwrs, $owner);
+ if ($status != 0) return -5;
- if ($comment != '' && $this->hasSharedComments()) {
- $status = $this->setComment('TABLESPACE',$spcname,'',$comment);
- if ($status != 0) return -2;
- }
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterViewName($vwrs, $name);
+ if ($status != 0) return -3;
return 0;
-
}
- /**
- * Drops a tablespace
- * @param $spcname The name of the domain to drop
- * @return 0 success
- */
- function dropTablespace($spcname) {
- $this->fieldClean($spcname);
-
- $sql = "DROP TABLESPACE \"{$spcname}\"";
-
- return $this->execute($sql);
- }
+ // Sequence functions
/**
- * Alters a tablespace
- * @param $spcname The name of the tablespace
- * @param $name The new name for the tablespace
- * @param $owner The new owner for the tablespace
+ * Protected method which alter a sequence
+ * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
+ * @param $seqrs The sequence recordSet returned by getSequence()
+ * @param $name The new name for the sequence
+ * @param $comment The comment on the sequence
+ * @param $owner The new owner for the sequence
+ * @param $schema The new schema for the sequence
+ * @param $increment The increment
+ * @param $minvalue The min value
+ * @param $maxvalue The max value
+ * @param $startvalue The starting value
+ * @param $cachevalue The cache value
+ * @param $cycledvalue True if cycled, false otherwise
* @return 0 success
- * @return -1 transaction error
- * @return -2 owner error
* @return -3 rename error
* @return -4 comment error
+ * @return -5 owner error
+ * @return -6 get sequence props error
*/
- function alterTablespace($spcname, $name, $owner, $comment='') {
- $this->fieldClean($spcname);
- $this->fieldClean($name);
- $this->fieldClean($owner);
+ protected
+ function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
+ $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
- // Begin transaction
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
+ /* $schema not supported in pg80- */
+ $this->fieldArrayClean($seqrs->fields);
- // Owner
- $sql = "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
- }
+ // Comment
+ $this->clean($comment);
+ $status = $this->setComment('SEQUENCE', $seqrs->fields['seqname'], '', $comment);
+ if ($status != 0)
+ return -4;
- // Rename (only if name has changed)
- if ($name != $spcname) {
- $sql = "ALTER TABLESPACE \"{$spcname}\" RENAME TO \"{$name}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
+ // Owner
+ $this->fieldClean($owner);
+ $status = $this->alterSequenceOwner($seqrs, $owner);
+ if ($status != 0)
+ return -5;
+
+ // Props
+ $this->clean($increment);
+ $this->clean($minvalue);
+ $this->clean($maxvalue);
+ $this->clean($startvalue);
+ $this->clean($cachevalue);
+ $this->clean($cycledvalue);
+ $status = $this->alterSequenceProps($seqrs, $increment, $minvalue,
+ $maxvalue, $startvalue, $cachevalue, $cycledvalue);
+ if ($status != 0)
+ return -6;
- // Set comment if it has changed
- if (trim($comment) != '' && $this->hasSharedComments()) {
- $status = $this->setComment('TABLESPACE',$spcname,'',$comment);
- if ($status != 0) return -4;
- }
+ // Rename
+ $this->fieldClean($name);
+ $status = $this->alterSequenceName($seqrs, $name);
+ if ($status != 0)
+ return -3;
- return $this->endTransaction();
+ return 0;
}
- // Backend process signalling functions
+ // Role, User/group functions
/**
- * Sends a cancel or kill command to a process
- * @param $pid The ID of the backend process
- * @param $signal 'CANCEL'
+ * Changes a user's password
+ * @param $username The username
+ * @param $password The new password
* @return 0 success
- * @return -1 invalid signal type
*/
- function sendSignal($pid, $signal) {
- // Clean
- $pid = (int)$pid;
+ function changePassword($username, $password) {
+ $enc = $this->_encryptPassword($username, $password);
+ $this->fieldClean($username);
+ $this->clean($enc);
- if ($signal == 'CANCEL')
- $sql = "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
- else
- return -1;
+ $sql = "ALTER USER \"{$username}\" WITH ENCRYPTED PASSWORD '{$enc}'";
- // Execute the query
- $val = $this->selectField($sql, 'val');
-
- if ($val === -1) return -1;
- elseif ($val == '1') return 0;
- else return -1;
+ return $this->execute($sql);
}
+ // Aggregate functions
+
/**
- * Returns all details for a particular function
- * @param $func The name of the function to retrieve
- * @return Function info
+ * Gets all information for an aggregate
+ * @param $name The name of the aggregate
+ * @param $basetype The input data type of the aggregate
+ * @return A recordset
*/
- function getFunction($function_oid) {
- $this->clean($function_oid);
-
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- pg_catalog.pg_get_userbyid(proowner) AS proowner,
- nspname as proschema,
- lanname as prolanguage,
- pg_catalog.format_type(prorettype, NULL) as proresult,
- prosrc,
- probin,
- proretset,
- proisstrict,
- provolatile,
- prosecdef,
- pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
- proargnames AS proargnames,
- pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment
- FROM
- pg_catalog.pg_proc pc, pg_catalog.pg_language pl, pg_catalog.pg_namespace pn
- WHERE
- pc.oid = '{$function_oid}'::oid
- AND pc.prolang = pl.oid
- AND pc.pronamespace = pn.oid
- ";
+ function getAggregate($name, $basetype) {
+ $this->fieldclean($name);
+ $this->fieldclean($basetype);
+
+ $sql = "
+ SELECT p.proname,
+ CASE p.proargtypes[0]
+ WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL
+ ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
+ END AS proargtypes, a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype,
+ a.aggfinalfn, a.agginitval, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
+ FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
+ WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
+ AND p.proisagg AND n.nspname='{$this->_schema}'
+ AND p.proname='" . $name . "'
+ AND CASE p.proargtypes[0]
+ WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN ''
+ ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
+ END ='" . $basetype . "'";
return $this->selectSet($sql);
}
// Capabilities
- function hasAlterDatabaseOwner() { return true; }
- function hasAlterColumnType() { return true; }
- function hasTablespaces() { return true; }
- function hasSignals() { return true; }
- function hasNamedParams() { return true; }
- function hasFunctionAlterOwner() { return true; }
+ function hasAggregateSortOp() { return false; }
+ function hasAlterTableSchema() { return false; }
+ function hasAutovacuum() { return false; }
+ function hasDisableTriggers() { return false; }
+ function hasFunctionAlterSchema() { return false; }
+ function hasPreparedXacts() { return false; }
+ function hasRoles() { return false; }
+ function hasSequenceAlterSchema() { return false; }
+ function hasServerAdminFuncs() { return false; }
}
?>
diff --git a/classes/database/Postgres81.php b/classes/database/Postgres81.php
index 8bcded4b..3701c68d 100644
--- a/classes/database/Postgres81.php
+++ b/classes/database/Postgres81.php
@@ -6,61 +6,47 @@
* $Id: Postgres81.php,v 1.21 2008/01/19 13:46:15 ioguix Exp $
*/
-include_once('./classes/database/Postgres80.php');
+include_once('./classes/database/Postgres82.php');
-class Postgres81 extends Postgres80 {
+class Postgres81 extends Postgres82 {
var $major_version = 8.1;
-
- // Map of database encoding names to HTTP encoding names. If a
- // database encoding does not appear in this list, then its HTTP
- // encoding name is the same as its database encoding name.
- var $codemap = array(
- 'BIG5' => 'BIG5',
- 'EUC_CN' => 'GB2312',
- 'EUC_JP' => 'EUC-JP',
- 'EUC_KR' => 'EUC-KR',
- 'EUC_TW' => 'EUC-TW',
- 'GB18030' => 'GB18030',
- 'GBK' => 'GB2312',
- 'ISO_8859_5' => 'ISO-8859-5',
- 'ISO_8859_6' => 'ISO-8859-6',
- 'ISO_8859_7' => 'ISO-8859-7',
- 'ISO_8859_8' => 'ISO-8859-8',
- 'JOHAB' => 'CP1361',
- 'KOI8' => 'KOI8-R',
- 'LATIN1' => 'ISO-8859-1',
- 'LATIN2' => 'ISO-8859-2',
- 'LATIN3' => 'ISO-8859-3',
- 'LATIN4' => 'ISO-8859-4',
- 'LATIN5' => 'ISO-8859-9',
- 'LATIN6' => 'ISO-8859-10',
- 'LATIN7' => 'ISO-8859-13',
- 'LATIN8' => 'ISO-8859-14',
- 'LATIN9' => 'ISO-8859-15',
- 'LATIN10' => 'ISO-8859-16',
- 'SJIS' => 'SHIFT_JIS',
- 'SQL_ASCII' => 'US-ASCII',
- 'UHC' => 'WIN949',
- 'UTF8' => 'UTF-8',
- 'WIN866' => 'CP866',
- 'WIN874' => 'CP874',
- 'WIN1250' => 'CP1250',
- 'WIN1251' => 'CP1251',
- 'WIN1252' => 'CP1252',
- 'WIN1256' => 'CP1256',
- 'WIN1258' => 'CP1258'
+ // List of all legal privileges that can be applied to different types
+ // of objects.
+ var $privlist = array(
+ 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
+ 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
+ 'database' => array('CREATE', 'TEMPORARY', 'ALL PRIVILEGES'),
+ 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
+ 'language' => array('USAGE', 'ALL PRIVILEGES'),
+ 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES'),
+ 'tablespace' => array('CREATE', 'ALL PRIVILEGES')
);
-
- // Last oid assigned to a system object
- var $_lastSystemOID = 17231;
+ // List of characters in acl lists and the privileges they
+ // refer to.
+ var $privmap = array(
+ 'r' => 'SELECT',
+ 'w' => 'UPDATE',
+ 'a' => 'INSERT',
+ 'd' => 'DELETE',
+ 'R' => 'RULE',
+ 'x' => 'REFERENCES',
+ 't' => 'TRIGGER',
+ 'X' => 'EXECUTE',
+ 'U' => 'USAGE',
+ 'C' => 'CREATE',
+ 'T' => 'TEMPORARY'
+ );
+ // Array of allowed index types
+ var $typIndexes = array('BTREE', 'RTREE', 'GIST', 'HASH');
/**
* Constructor
* @param $conn The database connection
*/
function Postgres81($conn) {
- $this->Postgres80($conn);
+ $this->Postgres82($conn);
}
// Help functions
@@ -70,7 +56,8 @@ class Postgres81 extends Postgres80 {
return $this->help_page;
}
- // Database Functions
+ // Database functions
+
/**
* Returns all databases available on the server
* @return A list of databases, sorted alphabetically
@@ -111,562 +98,143 @@ class Postgres81 extends Postgres80 {
}
/**
- * Returns prepared transactions information
- * @param $database (optional) Find only prepared transactions executed in a specific database
- * @return A recordset
- */
- function getPreparedXacts($database = null) {
- if ($database === null)
- $sql = "SELECT * FROM pg_prepared_xacts";
- else {
- $this->clean($database);
- $sql = "SELECT transaction, gid, prepared, owner FROM pg_prepared_xacts WHERE database='{$database}'
- ORDER BY owner";
- }
-
- return $this->selectSet($sql);
- }
-
- // Roles
-
- /**
- * Returns all roles in the database cluster
- * @param $rolename (optional) The role name to exclude from the select
- * @return All roles
- */
- function getRoles($rolename = '') {
- $sql = 'SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit, rolcanlogin, rolconnlimit, rolvaliduntil,
- rolconfig FROM pg_catalog.pg_roles';
- if($rolename) $sql .= " WHERE rolname!='{$rolename}'";
- $sql .= ' ORDER BY rolname';
-
- return $this->selectSet($sql);
- }
-
- /**
- * Returns information about a single role
- * @param $rolename The name of the role to retrieve
- * @return The role's data
- */
- function getRole($rolename) {
- $this->clean($rolename);
-
- $sql = "SELECT rolname, rolsuper, rolcreatedb, rolcreaterole, rolinherit, rolcanlogin, rolconnlimit, rolvaliduntil,
- rolconfig FROM pg_catalog.pg_roles WHERE rolname='{$rolename}'";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates a new role
- * @param $rolename The name of the role to create
- * @param $password A password for the role
- * @param $superuser Boolean whether or not the role is a superuser
- * @param $createdb Boolean whether or not the role can create databases
- * @param $createrole Boolean whether or not the role can create other roles
- * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
- * @param $login Boolean whether or not the role will be allowed to login
- * @param $connlimit Number of concurrent connections the role can make
- * @param $expiry String Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
- * @param $memberof (array) Roles to which the new role will be immediately added as a new member
- * @param $members (array) Roles which are automatically added as members of the new role
- * @param $adminmembers (array) Roles which are automatically added as admin members of the new role
- * @return 0 success
- */
- function createRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers) {
- $enc = $this->_encryptPassword($rolename, $password);
- $this->fieldClean($rolename);
- $this->clean($enc);
- $this->clean($connlimit);
- $this->clean($expiry);
- $this->fieldArrayClean($memberof);
- $this->fieldArrayClean($members);
- $this->fieldArrayClean($adminmembers);
-
- $sql = "CREATE ROLE \"{$rolename}\"";
- if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
- $sql .= ($superuser) ? ' SUPERUSER' : ' NOSUPERUSER';
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createrole) ? ' CREATEROLE' : ' NOCREATEROLE';
- $sql .= ($inherits) ? ' INHERIT' : ' NOINHERIT';
- $sql .= ($login) ? ' LOGIN' : ' NOLOGIN';
- if ($connlimit != '') $sql .= " CONNECTION LIMIT {$connlimit}"; else $sql .= ' CONNECTION LIMIT -1';
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'"; else $sql .= " VALID UNTIL 'infinity'";
- if (is_array($memberof) && sizeof($memberof) > 0) $sql .= ' IN ROLE "' . join('", "', $memberof) . '"';
- if (is_array($members) && sizeof($members) > 0) $sql .= ' ROLE "' . join('", "', $members) . '"';
- if (is_array($adminmembers) && sizeof($adminmembers) > 0) $sql .= ' ADMIN "' . join('", "', $adminmembers) . '"';
-
- return $this->execute($sql);
- }
-
- /**
- * Removes a role
- * @param $rolename The name of the role to drop
- * @return 0 success
- */
- function dropRole($rolename) {
- $this->fieldClean($rolename);
-
- $sql = "DROP ROLE \"{$rolename}\"";
-
- return $this->execute($sql);
- }
-
- /**
- * Adjusts a role's info and renames it
- * @param $rolename The name of the role to adjust
- * @param $password A password for the role
- * @param $superuser Boolean whether or not the role is a superuser
- * @param $createdb Boolean whether or not the role can create databases
- * @param $createrole Boolean whether or not the role can create other roles
- * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
- * @param $login Boolean whether or not the role will be allowed to login
- * @param $connlimit Number of concurrent connections the role can make
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
- * @param $memberof (array) Roles to which the role will be immediately added as a new member
- * @param $members (array) Roles which are automatically added as members of the role
- * @param $adminmembers (array) Roles which are automatically added as admin members of the role
- * @param $memberofold (array) Original roles whose the role belongs to
- * @param $membersold (array) Original roles that are members of the role
- * @param $adminmembersold (array) Original roles that are admin members of the role
- * @param $newrolename The new name of the role
+ * Alters a database
+ * the multiple return vals are for postgres 8+ which support more functionality in alter database
+ * @param $dbName The name of the database
+ * @param $newName new name for the database
+ * @param $newOwner The new owner for the database
* @return 0 success
* @return -1 transaction error
- * @return -2 set role attributes error
+ * @return -2 owner error
* @return -3 rename error
*/
- function setRenameRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers, $memberofold, $membersold, $adminmembersold, $newrolename) {
+ function alterDatabase($dbName, $newName, $newOwner = '', $comment = '') {
+ $this->clean($dbName);
+ $this->clean($newName);
+ $this->clean($newOwner);
+ //ignore $comment, not supported pre 8.2
$status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $status = $this->setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers, $memberofold, $membersold, $adminmembersold);
if ($status != 0) {
$this->rollbackTransaction();
- return -2;
+ return -1;
}
- if ($rolename != $newrolename){
- $status = $this->renameRole($rolename, $newrolename);
+ if ($dbName != $newName) {
+ $status = $this->alterDatabaseRename($dbName, $newName);
if ($status != 0) {
$this->rollbackTransaction();
return -3;
}
}
- return $this->endTransaction();
- }
-
- /**
- * Adjusts a role's info
- * @param $rolename The name of the role to adjust
- * @param $password A password for the role
- * @param $superuser Boolean whether or not the role is a superuser
- * @param $createdb Boolean whether or not the role can create databases
- * @param $createrole Boolean whether or not the role can create other roles
- * @param $inherits Boolean whether or not the role inherits the privileges from parent roles
- * @param $login Boolean whether or not the role will be allowed to login
- * @param $connlimit Number of concurrent connections the role can make
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. '' means never expire
- * @param $memberof (array) Roles to which the role will be immediately added as a new member
- * @param $members (array) Roles which are automatically added as members of the role
- * @param $adminmembers (array) Roles which are automatically added as admin members of the role
- * @param $memberofold (array) Original roles whose the role belongs to
- * @param $membersold (array) Original roles that are members of the role
- * @param $adminmembersold (array) Original roles that are admin members of the role
- * @return 0 success
- */
- function setRole($rolename, $password, $superuser, $createdb, $createrole, $inherits, $login, $connlimit, $expiry, $memberof, $members, $adminmembers, $memberofold, $membersold, $adminmembersold) {
- $enc = $this->_encryptPassword($rolename, $password);
- $this->fieldClean($rolename);
- $this->clean($enc);
- $this->clean($connlimit);
- $this->clean($expiry);
- $this->fieldArrayClean($memberof);
- $this->fieldArrayClean($members);
- $this->fieldArrayClean($adminmembers);
-
- $sql = "ALTER ROLE \"{$rolename}\"";
- if ($password != '') $sql .= " WITH ENCRYPTED PASSWORD '{$enc}'";
- $sql .= ($superuser) ? ' SUPERUSER' : ' NOSUPERUSER';
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';
- $sql .= ($createrole) ? ' CREATEROLE' : ' NOCREATEROLE';
- $sql .= ($inherits) ? ' INHERIT' : ' NOINHERIT';
- $sql .= ($login) ? ' LOGIN' : ' NOLOGIN';
- if ($connlimit != '') $sql .= " CONNECTION LIMIT {$connlimit}"; else $sql .= ' CONNECTION LIMIT -1';
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'"; else $sql .= " VALID UNTIL 'infinity'";
-
- $status = $this->execute($sql);
-
- if ($status != 0) return -1;
-
- //memberof
- $old = explode(',', $memberofold);
- foreach ($memberof as $m) {
- if (!in_array($m, $old)) {
- $status = $this->grantRole($m, $rolename);
- if ($status != 0) return -1;
- }
- }
- if($memberofold)
- {
- foreach ($old as $o) {
- if (!in_array($o, $memberof)) {
- $status = $this->revokeRole($o, $rolename, 0, 'CASCADE');
- if ($status != 0) return -1;
- }
- }
- }
-
- //members
- $old = explode(',', $membersold);
- foreach ($members as $m) {
- if (!in_array($m, $old)) {
- $status = $this->grantRole($rolename, $m);
- if ($status != 0) return -1;
- }
- }
- if($membersold)
- {
- foreach ($old as $o) {
- if (!in_array($o, $members)) {
- $status = $this->revokeRole($rolename, $o, 0, 'CASCADE');
- if ($status != 0) return -1;
- }
- }
- }
-
- //adminmembers
- $old = explode(',', $adminmembersold);
- foreach ($adminmembers as $m) {
- if (!in_array($m, $old)) {
- $status = $this->grantRole($rolename, $m, 1);
- if ($status != 0) return -1;
- }
- }
- if($adminmembersold)
- {
- foreach ($old as $o) {
- if (!in_array($o, $adminmembers)) {
- $status = $this->revokeRole($rolename, $o, 1, 'CASCADE');
- if ($status != 0) return -1;
- }
- }
- }
-
- return $status;
- }
-
- /**
- * Renames a role
- * @param $rolename The name of the role to rename
- * @param $newrolename The new name of the role
- * @return 0 success
- */
- function renameRole($rolename, $newrolename){
- $this->fieldClean($rolename);
- $this->fieldClean($newrolename);
-
- $sql = "ALTER ROLE \"{$rolename}\" RENAME TO \"{$newrolename}\"";
-
- return $this->execute($sql);
+ $status = $this->alterDatabaseOwner($newName, $newOwner);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
}
-
- /**
- * Grants membership in a role
- * @param $role The name of the target role
- * @param $rolename The name of the role that will belong to the target role
- * @param $admin (optional) Flag to grant the admin option
- * @return 0 success
- */
- function grantRole($role, $rolename, $admin=0) {
- $this->fieldClean($role);
- $this->fieldClean($rolename);
-
- $sql = "GRANT \"{$role}\" TO \"{$rolename}\"";
- if($admin == 1) $sql .= ' WITH ADMIN OPTION';
-
- return $this->execute($sql);
+ return $this->endTransaction();
}
-
- /**
- * Revokes membership in a role
- * @param $role The name of the target role
- * @param $rolename The name of the role that will not belong to the target role
- * @param $admin (optional) Flag to revoke only the admin option
- * @param $type (optional) Type of revoke: RESTRICT | CASCADE
- * @return 0 success
- */
- function revokeRole($role, $rolename, $admin = 0, $type = 'RESTRICT') {
- $this->fieldClean($role);
- $this->fieldClean($rolename);
- $sql = "REVOKE ";
- if($admin == 1) $sql .= 'ADMIN OPTION FOR ';
- $sql .= "\"{$role}\" FROM \"{$rolename}\" {$type}";
-
- return $this->execute($sql);
- }
+ // Constraint functions
/**
- * Changes a role's password
- * @param $rolename The role name
- * @param $password The new password
- * @return 0 success
+ * Returns a list of all constraints on a table,
+ * including constraint name, definition, related col and referenced namespace,
+ * table and col if needed
+ * @param $table the table where we are looking for fk
+ * @return a recordset
*/
- function changePassword($rolename, $password) {
- $enc = $this->_encryptPassword($rolename, $password);
- $this->fieldClean($rolename);
- $this->clean($enc);
-
- $sql = "ALTER ROLE \"{$rolename}\" WITH ENCRYPTED PASSWORD '{$enc}'";
-
- return $this->execute($sql);
- }
+ function getConstraintsWithFields($table) {
+ global $data;
- /**
- * Returns all role names which the role belongs to
- * @param $rolename The role name
- * @return All role names
- */
- function getMemberOf($rolename) {
- $this->clean($rolename);
+ $data->clean($table);
- $sql = "SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M WHERE R.oid=M.roleid
- AND member IN (SELECT oid FROM pg_catalog.pg_roles WHERE rolname='{$rolename}') ORDER BY rolname";
+ // get the max number of col used in a constraint for the table
+ $sql = "SELECT DISTINCT
+ max(SUBSTRING(array_dims(c.conkey) FROM '^\\\[.*:(.*)\\\]$')) as nb
+ FROM
+ pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
+ JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
+ WHERE
+ r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
- return $this->selectSet($sql);
- }
+ $rs = $this->selectSet($sql);
- /**
- * Returns all role names that are members of a role
- * @param $rolename The role name
- * @param $admin (optional) Find only admin members
- * @return All role names
- */
- function getMembers($rolename, $admin = 'f') {
- $this->clean($rolename);
+ if ($rs->EOF) $max_col = 0;
+ else $max_col = $rs->fields['nb'];
- $sql = "SELECT rolname FROM pg_catalog.pg_roles R, pg_auth_members M WHERE R.oid=M.member AND admin_option='{$admin}'
- AND roleid IN (SELECT oid FROM pg_catalog.pg_roles WHERE rolname='{$rolename}') ORDER BY rolname";
+ $sql = '
+ SELECT
+ c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc,
+ ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
+ r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
+ pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
+ FROM
+ pg_catalog.pg_constraint AS c
+ JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
+ JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
+ for ($i = 2; $i <= $rs->fields['nb']; $i++) {
+ $sql.= " OR f1.attnum=c.conkey[$i]";
+ }
+ $sql.= '))
+ JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
+ LEFT JOIN (
+ pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
+ ) ON (c.confrelid=r2.oid)
+ LEFT JOIN pg_catalog.pg_attribute AS f2 ON
+ (f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
+ for ($i = 2; $i <= $rs->fields['nb']; $i++)
+ $sql.= "OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
+
+ $sql .= sprintf("))
+ WHERE
+ r1.relname = '%s' AND ns1.nspname='%s'
+ ORDER BY 1", $table, $this->_schema);
return $this->selectSet($sql);
}
- // Database methods
+ // Tablespace functions
/**
- * Returns all available process information.
+ * Retrieves a tablespace's information
* @return A recordset
*/
- function getAutovacuum() {
- $sql = "SELECT vacrelid, nspname, relname, enabled, vac_base_thresh, vac_scale_factor, anl_base_thresh, anl_scale_factor, vac_cost_delay, vac_cost_limit
- FROM pg_autovacuum join pg_class on (oid=vacrelid) join pg_namespace on (oid=relnamespace) ORDER BY nspname, relname";
-
- return $this->selectSet($sql);
- }
-
- // Table methods
-
- /**
- * Protected method which alter a table
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $tblrs The table recordSet returned by getTable()
- * @param $name The new name for the table
- * @param $owner The new owner for the table
- * @param $schema The new schema for the table
- * @param $comment The comment on the table
- * @param $tablespace The new tablespace for the table ('' means leave as is)
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 tablespace error
- * @return -7 schema error
- */
- /* protected */
- function _alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace) {
-
- $status = parent::_alterTable($tblrs, $name, $owner, $schema, $comment, $tablespace);
- if ($status != 0)
- return $status;
-
- // if name != tablename, table has been renamed in parent
- $tablename = ($tblrs->fields['relname'] == $name) ? $tblrs->fields['relname'] : $name;
-
- $this->fieldClean($schema);
-
- // Schema
- if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
-
- // If tablespace has been changed, then do the alteration. We
- // don't want to do this unnecessarily.
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$tablename}\" SET SCHEMA \"{$schema}\"";
-
- $status = $this->execute($sql);
- if ($status != 0) return -7;
- }
-
- return 0;
- }
-
- /**
- * Enables a trigger
- * @param $tgname The name of the trigger to enable
- * @param $table The table in which to enable the trigger
- * @return 0 success
- */
- function enableTrigger($tgname, $table) {
- $this->fieldClean($tgname);
- $this->fieldClean($table);
-
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" ENABLE TRIGGER \"{$tgname}\"";
-
- return $this->execute($sql);
- }
-
- /**
- * Disables a trigger
- * @param $tgname The name of the trigger to disable
- * @param $table The table in which to disable the trigger
- * @return 0 success
- */
- function disableTrigger($tgname, $table) {
- $this->fieldClean($tgname);
- $this->fieldClean($table);
+ function getTablespace($spcname) {
+ $this->clean($spcname);
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$table}\" DISABLE TRIGGER \"{$tgname}\"";
+ $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation
+ FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
- return $this->execute($sql);
- }
-
- // View functions
-
- /**
- * Protected method which alter a view
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $vwrs The view recordSet returned by getView()
- * @param $name The new name for the view
- * @param $owner The new owner for the view
- * @param $comment The comment on the view
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 schema error
- */
- /*protected*/
- function _alterView($vwrs, $name, $owner, $schema, $comment) {
-
- $status = parent::_alterView($vwrs, $name, $owner, $schema, $comment);
- if ($status != 0)
- return $status;
-
- $this->fieldClean($name);
- // if name is not empty, view has been renamed in parent
- $view = (!empty($name)) ? $name : $tblrs->fields['relname'];
-
- // Schema
- $this->fieldClean($schema);
- if (!empty($schema) && ($vwrs->fields['nspname'] != $schema)) {
-
- // If tablespace has been changed, then do the alteration. We
- // don't want to do this unnecessarily.
- $sql = "ALTER TABLE \"{$this->_schema}\".\"{$view}\" SET SCHEMA \"{$schema}\"";
-
- $status = $this->execute($sql);
- if ($status != 0) return -6;
- }
-
- return 0;
+ return $this->selectSet($sql);
}
- // Sequence methods
-
/**
- * Protected method which alter a sequence
- * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
- * @param $seqrs The sequence recordSet returned by getSequence()
- * @param $name The new name for the sequence
- * @param $comment The comment on the sequence
- * @param $owner The new owner for the sequence
- * @param $schema The new schema for the sequence
- * @param $increment The increment
- * @param $minvalue The min value
- * @param $maxvalue The max value
- * @param $startvalue The starting value
- * @param $cachevalue The cache value
- * @param $cycledvalue True if cycled, false otherwise
- * @return 0 success
- * @return -3 rename error
- * @return -4 comment error
- * @return -5 owner error
- * @return -6 get sequence error
- * @return -7 schema error
+ * Retrieves information for all tablespaces
+ * @param $all Include all tablespaces (necessary when moving objects back to the default space)
+ * @return A recordset
*/
- /*protected*/
- function _alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue) {
-
- $status = parent::_alterSequence($seqrs, $name, $comment, $owner, $schema, $increment,
- $minvalue, $maxvalue, $startvalue, $cachevalue, $cycledvalue);
- if ($status != 0)
- return $status;
+ function getTablespaces($all = false) {
+ global $conf;
- // if name != seqname, sequence has been renamed in parent
- $sequence = ($seqrs->fields['seqname'] == $name) ? $seqrs->fields['seqname'] : $name;
-
- $this->clean($schema);
- if ($seqrs->fields['nspname'] != $schema) {
- $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$sequence}\" SET SCHEMA $schema";
- $status = $this->execute($sql);
- if ($status != 0)
- return -7;
- }
+ $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation
+ FROM pg_catalog.pg_tablespace";
- return 0;
+ if (!$conf['show_system'] && !$all) {
+ $sql .= " WHERE spcname NOT LIKE 'pg\\\\_%'";
}
- // Aggregate functions
-
- /**
- * Gets all information for an aggregate
- * @param $name The name of the aggregate
- * @param $basetype The input data type of the aggregate
- * @return A recordset
- */
- function getAggregate($name, $basetype) {
- $this->fieldclean($name);
- $this->fieldclean($basetype);
-
- $sql = "
- SELECT p.proname, CASE p.proargtypes[0]
- WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN NULL
- ELSE pg_catalog.format_type(p.proargtypes[0], NULL) END AS proargtypes,
- a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn,
- a.agginitval, a.aggsortop, u.usename, pg_catalog.obj_description(p.oid, 'pg_proc') AS aggrcomment
- FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_user u, pg_catalog.pg_aggregate a
- WHERE n.oid = p.pronamespace AND p.proowner=u.usesysid AND p.oid=a.aggfnoid
- AND p.proisagg AND n.nspname='{$this->_schema}'
- AND p.proname='" . $name . "'
- AND CASE p.proargtypes[0]
- WHEN 'pg_catalog.\"any\"'::pg_catalog.regtype THEN ''
- ELSE pg_catalog.format_type(p.proargtypes[0], NULL)
- END ='" . $basetype . "'";
+ $sql .= " ORDER BY spcname";
return $this->selectSet($sql);
}
// Capabilities
- function hasServerAdminFuncs() { return true; }
- function hasRoles() { return true; }
- function hasAutovacuum() { return true; }
- function hasPreparedXacts() { return true; }
- function hasDisableTriggers() { return true; }
- function hasFunctionAlterSchema() { return true; }
- function hasAlterTableSchema() { return true; }
- function hasSequenceAlterSchema() { return true; }
- function hasAggregateSortOp() { return true; }
+
+ function hasCreateTableLikeWithConstraints() {return false;}
+ function hasSharedComments() {return false;}
}
?>
diff --git a/classes/database/Postgres82.php b/classes/database/Postgres82.php
index 5615a663..89de0399 100644
--- a/classes/database/Postgres82.php
+++ b/classes/database/Postgres82.php
@@ -6,54 +6,24 @@
* $Id: Postgres82.php,v 1.10 2007/12/28 16:21:25 ioguix Exp $
*/
-include_once('./classes/database/Postgres81.php');
+include_once('./classes/database/Postgres.php');
-class Postgres82 extends Postgres81 {
+class Postgres82 extends Postgres {
var $major_version = 8.2;
- // Array of allowed index types
- var $typIndexes = array('BTREE', 'RTREE', 'GIST', 'GIN', 'HASH');
-
- // Last oid assigned to a system object
- var $_lastSystemOID = 17231;
-
- // List of all legal privileges that can be applied to different types
- // of objects.
- var $privlist = array(
- 'table' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'view' => array('SELECT', 'INSERT', 'UPDATE', 'DELETE', 'RULE', 'REFERENCES', 'TRIGGER', 'ALL PRIVILEGES'),
- 'sequence' => array('SELECT', 'UPDATE', 'ALL PRIVILEGES'),
- 'database' => array('CREATE', 'TEMPORARY', 'CONNECT', 'ALL PRIVILEGES'),
- 'function' => array('EXECUTE', 'ALL PRIVILEGES'),
- 'language' => array('USAGE', 'ALL PRIVILEGES'),
- 'schema' => array('CREATE', 'USAGE', 'ALL PRIVILEGES'),
- 'tablespace' => array('CREATE', 'ALL PRIVILEGES')
- );
-
- // List of characters in acl lists and the privileges they
- // refer to.
- var $privmap = array(
- 'r' => 'SELECT',
- 'w' => 'UPDATE',
- 'a' => 'INSERT',
- 'd' => 'DELETE',
- 'R' => 'RULE',
- 'x' => 'REFERENCES',
- 't' => 'TRIGGER',
- 'X' => 'EXECUTE',
- 'U' => 'USAGE',
- 'C' => 'CREATE',
- 'T' => 'TEMPORARY',
- 'c' => 'CONNECT'
- );
+ // Select operators
+ var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i', '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
+ 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', 'SIMILAR TO' => 'i',
+ 'NOT SIMILAR TO' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
+ 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x');
/**
* Constructor
* @param $conn The database connection
*/
function Postgres82($conn) {
- $this->Postgres81($conn);
+ $this->Postgres($conn);
}
// Help functions
@@ -64,205 +34,240 @@ class Postgres82 extends Postgres81 {
}
// Database functions
+
/**
- * Return all database available on the server
- * @return A list of databases, sorted alphabetically
+ * Returns table locks information in the current database
+ * @return A recordset
*/
- function getDatabases($currentdatabase = NULL) {
- global $conf, $misc;
-
- $server_info = $misc->getServerInfo();
-
- if (isset($conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
- $username = $server_info['username'];
- $this->clean($username);
- $clause = " AND pr.rolname='{$username}'";
- }
- else $clause = '';
-
- if ($currentdatabase != NULL)
- $orderby = "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
- else
- $orderby = "ORDER BY pdb.datname";
+ function getLocks() {
+ global $conf;
if (!$conf['show_system'])
- $where = ' AND NOT pdb.datistemplate';
+ $where = "AND pn.nspname NOT LIKE 'pg\\\\_%'";
else
- $where = ' AND pdb.datallowconn';
-
- $sql = "SELECT pdb.datname AS datname, pr.rolname AS datowner, pg_encoding_to_char(encoding) AS datencoding,
- (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pdb.oid=pd.objoid) AS datcomment,
- (SELECT spcname FROM pg_catalog.pg_tablespace pt WHERE pt.oid=pdb.dattablespace) AS tablespace,
- pg_catalog.pg_database_size(pdb.oid) as dbsize
- FROM pg_catalog.pg_database pdb LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
- WHERE true
- {$where}
- {$clause}
- {$orderby}";
+ $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
+
+ $sql = "SELECT pn.nspname, pc.relname AS tablename, pl.transaction, pl.pid, pl.mode, pl.granted
+ FROM pg_catalog.pg_locks pl, pg_catalog.pg_class pc, pg_catalog.pg_namespace pn
+ WHERE pl.relation = pc.oid AND pc.relnamespace=pn.oid {$where}
+ ORDER BY nspname,tablename";
return $this->selectSet($sql);
}
+ // Sequence functions
+
/**
- * Alters a database
- * the multiple return vals are for postgres 8+ which support more functionality in alter database
- * @param $dbName The name of the database
- * @param $newName new name for the database
- * @param $newOwner The new owner for the database
+ * Rename a sequence
+ * @param $seqrs The sequence RecordSet returned by getSequence()
+ * @param $name The new name for the sequence
* @return 0 success
- * @return -1 transaction error
- * @return -2 owner error
- * @return -3 rename error
- * @return -4 comment error
*/
- function alterDatabase($dbName, $newName, $newOwner = '', $comment = '')
- {
- $this->clean($dbName);
- $this->clean($newName);
- $this->clean($newOwner);
- $this->clean($comment);
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
+ function alterSequenceName($seqrs, $name) {
+ if (!empty($name) && ($seqrs->fields['seqname'] != $name)) {
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$seqrs->fields['seqname']}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status == 0)
+ $seqrs->fields['seqname'] = $name;
+ else
+ return $status;
}
-
- if ($dbName != $newName) {
- $status = $this->alterDatabaseRename($dbName, $newName);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -3;
- }
- }
-
- $status = $this->alterDatabaseOwner($newName, $newOwner);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -2;
+ return 0;
}
- if (trim($comment) != '' ) {
- $status = $this->setComment('DATABASE', $dbName, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -4;
- }
- }
- return $this->endTransaction();
- }
+ // View functions
/**
- * Return the database comment of a db from the shared description table
- * @param string $database the name of the database to get the comment for
- * @return recordset of the db comment info
+ * Rename a view
+ * @param $vwrs The view recordSet returned by getView()
+ * @param $name The new view's name
+ * @return -1 Failed
+ * @return 0 success
*/
- function getDatabaseComment($database) {
- $this->clean($database);
- $sql = "SELECT description FROM pg_catalog.pg_database JOIN pg_catalog.pg_shdescription ON (oid=objoid) WHERE pg_database.datname = '{$database}' ";
- return $this->selectSet($sql);
+ function alterViewName($vwrs, $name) {
+ // Rename (only if name has changed)
+ if (!empty($name) && ($name != $vwrs->fields['relname'])) {
+ $sql = "ALTER TABLE \"{$this->_schema}\".\"{$vwrs->fields['relname']}\" RENAME TO \"{$name}\"";
+ $status = $this->execute($sql);
+ if ($status == 0)
+ $vwrs->fields['relname'] = $name;
+ else
+ return $status;
+ }
+ return 0;
}
- // Tablespace functions
+ // Trigger functions
/**
- * Retrieves information for all tablespaces
- * @param $all Include all tablespaces (necessary when moving objects back to the default space)
+ * Grabs a list of triggers on a table
+ * @param $table The name of a table whose triggers to retrieve
* @return A recordset
*/
- function getTablespaces($all = false) {
- global $conf;
+ function getTriggers($table = '') {
+ $this->clean($table);
+
+ $sql = "SELECT
+ t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef, t.tgenabled, p.oid AS prooid,
+ p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
+ ns.nspname AS pronamespace
+ FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
+ WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
+ AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
+ AND (NOT tgisconstraint OR NOT EXISTS
+ (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
+ ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
+ WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
+ AND p.oid=t.tgfoid
+ AND p.pronamespace = ns.oid";
- $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation,
- (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid) AS spccomment
- FROM pg_catalog.pg_tablespace";
-
- if (!$conf['show_system'] && !$all) {
- $sql .= " WHERE spcname NOT LIKE 'pg\\\\_%'";
+ return $this->selectSet($sql);
}
- $sql .= " ORDER BY spcname";
+ // Function functions
+
+ /**
+ * Returns all details for a particular function
+ * @param $func The name of the function to retrieve
+ * @return Function info
+ */
+ function getFunction($function_oid) {
+ $this->clean($function_oid);
+
+ $sql = "SELECT
+ pc.oid AS prooid,
+ proname,
+ pg_catalog.pg_get_userbyid(proowner) AS proowner,
+ nspname as proschema,
+ lanname as prolanguage,
+ pg_catalog.format_type(prorettype, NULL) as proresult,
+ prosrc,
+ probin,
+ proretset,
+ proisstrict,
+ provolatile,
+ prosecdef,
+ pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
+ proargnames AS proargnames,
+ pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment
+ FROM
+ pg_catalog.pg_proc pc, pg_catalog.pg_language pl, pg_catalog.pg_namespace pn
+ WHERE
+ pc.oid = '{$function_oid}'::oid
+ AND pc.prolang = pl.oid
+ AND pc.pronamespace = pn.oid
+ ";
return $this->selectSet($sql);
}
/**
- * Retrieves a tablespace's information
- * @return A recordset
+ * Creates a new function.
+ * @param $funcname The name of the function to create
+ * @param $args A comma separated string of types
+ * @param $returns The return type
+ * @param $definition The definition for the new function
+ * @param $language The language the function is written for
+ * @param $flags An array of optional flags
+ * @param $setof True if it returns a set, false otherwise
+ * @param $replace (optional) True if OR REPLACE, false for normal
+ * @return 0 success
*/
- function getTablespace($spcname) {
- $this->clean($spcname);
+ function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $replace = false) {
+ $this->fieldClean($funcname);
+ $this->clean($args);
+ $this->clean($language);
+ $this->arrayClean($flags);
+
+ $sql = "CREATE";
+ if ($replace) $sql .= " OR REPLACE";
+ $sql .= " FUNCTION \"{$this->_schema}\".\"{$funcname}\" (";
+
+ if ($args != '')
+ $sql .= $args;
+
+ // For some reason, the returns field cannot have quotes...
+ $sql .= ") RETURNS ";
+ if ($setof) $sql .= "SETOF ";
+ $sql .= "{$returns} AS ";
+
+ if (is_array($definition)) {
+ $this->arrayClean($definition);
+ $sql .= "'" . $definition[0] . "'";
+ if ($definition[1]) {
+ $sql .= ",'" . $definition[1] . "'";
+ }
+ } else {
+ $this->clean($definition);
+ $sql .= "'" . $definition . "'";
+ }
- $sql = "SELECT spcname, pg_catalog.pg_get_userbyid(spcowner) AS spcowner, spclocation,
- (SELECT description FROM pg_catalog.pg_shdescription pd WHERE pg_tablespace.oid=pd.objoid) AS spccomment
- FROM pg_catalog.pg_tablespace WHERE spcname='{$spcname}'";
+ $sql .= " LANGUAGE \"{$language}\"";
- return $this->selectSet($sql);
+ // Add flags
+ foreach ($flags as $v) {
+ // Skip default flags
+ if ($v == '') continue;
+ else $sql .= "\n{$v}";
+ }
+
+ return $this->execute($sql);
}
- // Constraints methods
+ // Index functions
/**
- * Returns a list of all constraints on a table,
- * including constraint name, definition, related col and referenced namespace,
- * table and col if needed
- * @param $table the table where we are looking for fk
- * @return a recordset
+ * Clusters an index
+ * @param $index The name of the index
+ * @param $table The table the index is on
+ * @return 0 success
*/
- function getConstraintsWithFields($table) {
- global $data;
-
- $data->clean($table);
+ function clusterIndex($index, $table) {
+ $this->fieldClean($index);
+ $this->fieldClean($table);
- // get the max number of col used in a constraint for the table
- $sql = "SELECT DISTINCT
- max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb
- FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid)
- JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid
- WHERE
- r.relname = '$table' AND ns.nspname='". $this->_schema ."'";
+ // We don't bother with a transaction here, as there's no point rolling
+ // back an expensive cluster if a cheap analyze fails for whatever reason
+ $sql = "CLUSTER \"{$index}\" ON \"{$this->_schema}\".\"{$table}\"";
- $rs = $this->selectSet($sql);
+ return $this->execute($sql);
+ }
- if ($rs->EOF) $max_col = 0;
- else $max_col = $rs->fields['nb'];
+ // Operator Class functions
- $sql = '
+ /**
+ * Gets all opclasses
+ * @return A recordset
+ */
+ function getOpClasses() {
+ $sql = "
SELECT
- c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc,
- ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema,
- r2.relname as f_table, f1.attname as p_field, f2.attname as f_field,
- pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment
+ pa.amname,
+ po.opcname,
+ po.opcintype::pg_catalog.regtype AS opcintype,
+ po.opcdefault,
+ pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
FROM
- pg_catalog.pg_constraint AS c
- JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid)
- JOIN pg_catalog.pg_attribute AS f1 ON (f1.attrelid=r1.oid AND (f1.attnum=c.conkey[1]';
- for ($i = 2; $i <= $rs->fields['nb']; $i++) {
- $sql.= " OR f1.attnum=c.conkey[$i]";
- }
- $sql.= '))
- JOIN pg_catalog.pg_namespace AS ns1 ON r1.relnamespace=ns1.oid
- LEFT JOIN (
- pg_catalog.pg_class AS r2 JOIN pg_catalog.pg_namespace AS ns2 ON (r2.relnamespace=ns2.oid)
- ) ON (c.confrelid=r2.oid)
- LEFT JOIN pg_catalog.pg_attribute AS f2 ON
- (f2.attrelid=r2.oid AND ((c.confkey[1]=f2.attnum AND c.conkey[1]=f1.attnum)';
- for ($i = 2; $i <= $rs->fields['nb']; $i++)
- $sql.= "OR (c.confkey[$i]=f2.attnum AND c.conkey[$i]=f1.attnum)";
-
- $sql .= sprintf("))
+ pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
WHERE
- r1.relname = '%s' AND ns1.nspname='%s'
- ORDER BY 1", $table, $this->_schema);
+ po.opcamid=pa.oid
+ AND po.opcnamespace=pn.oid
+ AND pn.nspname='{$this->_schema}'
+ ORDER BY 1,2
+ ";
return $this->selectSet($sql);
}
// Capabilities
- function hasSharedComments() {return true;}
- function hasCreateTableLikeWithConstraints() {return true;}
+
+ function hasCreateTableLikeWithIndexes() {return false;}
+ function hasEnumTypes() {return false;}
+ function hasFTS() {return false;}
+ function hasFunctionCosting() {return false;}
+ function hasFunctionGUC() {return false;}
+ function hasVirtualTransactionId() {return false;}
+
}
?>
diff --git a/classes/database/Postgres83.php b/classes/database/Postgres83.php
deleted file mode 100644
index e999868b..00000000
--- a/classes/database/Postgres83.php
+++ /dev/null
@@ -1,798 +0,0 @@
-<?php
-
-/**
- * PostgreSQL 8.3 support
- *
- * $Id: Postgres83.php,v 1.18 2008/03/17 21:35:48 ioguix Exp $
- */
-
-include_once('./classes/database/Postgres82.php');
-
-class Postgres83 extends Postgres82 {
-
- var $major_version = 8.3;
-
- // Last oid assigned to a system object
- var $_lastSystemOID = 17231; // need to confirm this once we get to beta!!!
-
- // Select operators
- var $selectOps = array('=' => 'i', '!=' => 'i', '<' => 'i', '>' => 'i', '<=' => 'i', '>=' => 'i',
- '<<' => 'i', '>>' => 'i', '<<=' => 'i', '>>=' => 'i',
- 'LIKE' => 'i', 'NOT LIKE' => 'i', 'ILIKE' => 'i', 'NOT ILIKE' => 'i', 'SIMILAR TO' => 'i',
- 'NOT SIMILAR TO' => 'i', '~' => 'i', '!~' => 'i', '~*' => 'i', '!~*' => 'i',
- 'IS NULL' => 'p', 'IS NOT NULL' => 'p', 'IN' => 'x', 'NOT IN' => 'x',
- '@@' => 'i', '@@@' => 'i', '@>' => 'i', '<@' => 'i',
- '@@ to_tsquery' => 't', '@@@ to_tsquery' => 't', '@> to_tsquery' => 't', '<@ to_tsquery' => 't',
- '@@ plainto_tsquery' => 't', '@@@ plainto_tsquery' => 't', '@> plainto_tsquery' => 't', '<@ plainto_tsquery' => 't');
-
- /**
- * Constructor
- * @param $conn The database connection
- */
- function Postgres83($conn) {
- $this->Postgres82($conn);
- }
-
- // Help functions
-
- function getHelpPages() {
- include_once('./help/PostgresDoc83.php');
- return $this->help_page;
- }
-
- // Schemas functions
- /**
- * Returns table locks information in the current database
- * @return A recordset
- */
-
- function getLocks() {
- global $conf;
-
- if (!$conf['show_system'])
- $where = "AND pn.nspname NOT LIKE 'pg\\\\_%'";
- else
- $where = "AND nspname !~ '^pg_t(emp_[0-9]+|oast)$'";
-
- $sql = "SELECT
- pn.nspname, pc.relname AS tablename, pl.pid, pl.mode, pl.granted, pl.virtualtransaction,
- (select transactionid from pg_catalog.pg_locks l2 where l2.locktype='transactionid'
- and l2.mode='ExclusiveLock' and l2.virtualtransaction=pl.virtualtransaction) as transaction
- FROM
- pg_catalog.pg_locks pl,
- pg_catalog.pg_class pc,
- pg_catalog.pg_namespace pn
- WHERE
- pl.relation = pc.oid
- AND
- pc.relnamespace=pn.oid
- {$where}
- ORDER BY
- pid,nspname,tablename";
-
- return $this->selectSet($sql);
- }
-
- // Views functions
-
- /**
- * Rename a view
- * @param $view The current view's name
- * @param $name The new view's name
- * @return -1 Failed
- * @return 0 success
- */
- function renameView($view, $name) {
- $this->fieldClean($name);
- $this->fieldClean($view);
- $sql = "ALTER VIEW \"{$this->_schema}\".\"{$view}\" RENAME TO \"{$name}\"";
- if ($this->execute($sql) != 0)
- return -1;
- return 0;
- }
-
- // Index functions
-
- /**
- * Clusters an index
- * @param $index The name of the index
- * @param $table The table the index is on
- * @return 0 success
- */
- function clusterIndex($index, $table) {
-
- $this->fieldClean($index);
- $this->fieldClean($table);
-
- // We don't bother with a transaction here, as there's no point rolling
- // back an expensive cluster if a cheap analyze fails for whatever reason
- $sql = "CLUSTER \"{$this->_schema}\".\"{$table}\" USING \"{$index}\"";
-
- return $this->execute($sql);
- }
-
- // Sequence functions
-
- /**
- * Rename a sequence
- * @param $sequence The sequence name
- * @param $name The new name for the sequence
- * @return 0 success
- */
- function renameSequence($sequence, $name) {
- $this->fieldClean($name);
- $this->fieldClean($sequence);
-
- $sql = "ALTER SEQUENCE \"{$this->_schema}\".\"{$sequence}\" RENAME TO \"{$name}\"";
- return $this->execute($sql);
- }
-
- // Operator Class functions
-
- /**
- * Gets all opclasses
- *
- * * @return A recordset
-
- */
-
- function getOpClasses() {
-
- $sql = "
- SELECT
- pa.amname, po.opcname,
- po.opcintype::pg_catalog.regtype AS opcintype,
- po.opcdefault,
- pg_catalog.obj_description(po.oid, 'pg_opclass') AS opccomment
- FROM
- pg_catalog.pg_opclass po, pg_catalog.pg_am pa, pg_catalog.pg_namespace pn
- WHERE
- po.opcmethod=pa.oid
- AND po.opcnamespace=pn.oid
- AND pn.nspname='{$this->_schema}'
- ORDER BY 1,2
- ";
-
- return $this->selectSet($sql);
- }
-
- // FTS functions
-
- /**
- * Creates a new FTS configuration.
- * @param string $cfgname The name of the FTS configuration to create
- * @param string $parser The parser to be used in new FTS configuration
- * @param string $locale Locale of the FTS configuration
- * @param string $template The existing FTS configuration to be used as template for the new one
- * @param string $withmap Should we copy whole map of existing FTS configuration to the new one
- * @param string $makeDefault Should this configuration be the default for locale given
- * @param string $comment If omitted, defaults to nothing
- * @return 0 success
- */
- function createFtsConfiguration($cfgname, $parser = '', $template = '', $comment = '') {
- $this->fieldClean($cfgname);
-
- $sql = "CREATE TEXT SEARCH CONFIGURATION \"{$cfgname}\" (";
- if ($parser != '') {
- $this->fieldClean($parser['schema']);
- $this->fieldClean($parser['parser']);
- $parser = "\"{$parser['schema']}\".\"{$parser['parser']}\"";
- $sql .= " PARSER = {$parser}";
- }
- if ($template != '') {
- $this->fieldClean($template);
- $sql .= " COPY = \"{$template}\"";
- }
- $sql .= ")";
-
- if ($comment != '') {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
- }
-
- // Create the FTS configuration
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Set the comment
- if ($comment != '') {
- $this->clean($comment);
- $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- return $this->endTransaction();
- }
-
- return 0;
- }
-
-
- /**
- * Returns all FTS configurations available
- */
- function getFtsConfigurations() {
- $sql = "SELECT
- n.nspname as schema,
- c.cfgname as name,
- pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
- FROM
- pg_catalog.pg_ts_config c
- JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
- WHERE
- pg_catalog.pg_ts_config_is_visible(c.oid)
- ORDER BY
- schema, name";
- return $this->selectSet($sql);
- }
-
- /**
- * Return all information related to a FTS configuration
- * @param $ftscfg The name of the FTS configuration
- * @return FTS configuration information
- */
- function getFtsConfigurationByName($ftscfg) {
- $this->clean($ftscfg);
- $sql = "SELECT
- n.nspname as schema,
- c.cfgname as name,
- p.prsname as parser,
- c.cfgparser as parser_id,
- pg_catalog.obj_description(c.oid, 'pg_ts_config') as comment
- FROM pg_catalog.pg_ts_config c
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.cfgnamespace
- LEFT JOIN pg_catalog.pg_ts_parser p ON p.oid = c.cfgparser
- WHERE pg_catalog.pg_ts_config_is_visible(c.oid)
- AND c.cfgname = '{$ftscfg}'";
-
- return $this->selectSet($sql);
- }
-
-
- /**
- * Returns the map of FTS configuration given (list of mappings (tokens) and their processing dictionaries)
- *
- * @param string $ftscfg Name of the FTS configuration
- */
- function getFtsConfigurationMap($ftscfg) {
- $this->fieldClean($ftscfg);
- $getOidSql = "SELECT oid FROM pg_catalog.pg_ts_config WHERE cfgname = '{$ftscfg}'";
- $oidSet = $this->selectSet($getOidSql);
- $oid = $oidSet->fields['oid'];
-
- $sql = " SELECT
- (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
- (SELECT t.description FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS description,
- c.cfgname AS cfgname
- ,n.nspname ||'.'|| d.dictname as dictionaries
- FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d, pg_catalog.pg_namespace n
- WHERE c.oid = {$oid} AND m.mapcfg = c.oid and m.mapdict = d.oid and d.dictnamespace = n.oid
- ORDER BY name
- ";
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all FTS parsers available
- */
- function getFtsParsers() {
- $sql = "SELECT
- n.nspname as schema,
- p.prsname as name,
- pg_catalog.obj_description(p.oid, 'pg_ts_parser') as comment
- FROM pg_catalog.pg_ts_parser p
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = p.prsnamespace WHERE pg_catalog.pg_ts_parser_is_visible(p.oid)
- ORDER BY schema, name";
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all FTS dictionaries available
- */
- function getFtsDictionaries() {
- $sql = "SELECT
- n.nspname as schema,
- d.dictname as name,
- pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
- FROM pg_catalog.pg_ts_dict d
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
- WHERE pg_catalog.pg_ts_dict_is_visible(d.oid)
- ORDER BY schema, name;";
- return $this->selectSet($sql);
- }
-
- /**
- * Returns all FTS dictionary templates available
- */
- function getFtsDictionaryTemplates() {
- $sql = "SELECT
- n.nspname as schema,
- t.tmplname as name,
- ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname FROM
- pg_catalog.pg_proc p
- LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
- WHERE t.tmplinit = p.oid ) AS init,
- ( SELECT COALESCE(np.nspname, '(null)')::pg_catalog.text || '.' || p.proname FROM
- pg_catalog.pg_proc p
- LEFT JOIN pg_catalog.pg_namespace np ON np.oid = p.pronamespace
- WHERE t.tmpllexize = p.oid ) AS lexize,
- pg_catalog.obj_description(t.oid, 'pg_ts_template') as comment
- FROM pg_catalog.pg_ts_template t
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.tmplnamespace
- WHERE pg_catalog.pg_ts_template_is_visible(t.oid)
- ORDER BY schema, name;";
- return $this->selectSet($sql);
- }
-
- /**
- * Drops FTS coniguration
- */
- function dropFtsConfiguration($ftscfg, $cascade) {
- $this->fieldClean($ftscfg);
-
- $sql = "DROP TEXT SEARCH CONFIGURATION \"{$ftscfg}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Drops FTS dictionary
- *
- * @todo Support of dictionary templates dropping
- */
- function dropFtsDictionary($ftsdict, $cascade = true) {
- $this->fieldClean($ftsdict);
-
- $sql = "DROP TEXT SEARCH DICTIONARY";
- $sql .= " \"{$ftsdict}\"";
- if ($cascade) $sql .= " CASCADE";
-
- return $this->execute($sql);
- }
-
- /**
- * Alters FTS configuration
- */
- function updateFtsConfiguration($cfgname, $comment, $name) {
- $this->fieldClean($cfgname);
- $this->fieldClean($name);
- $this->clean($comment);
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- $status = $this->setComment('TEXT SEARCH CONFIGURATION', $cfgname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Only if the name has changed
- if ($name != $cfgname) {
- $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$cfgname}\" RENAME TO \"{$name}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- return $this->endTransaction();
- }
-
- /**
- * Creates a new FTS dictionary or FTS dictionary template.
- * @param string $dictname The name of the FTS dictionary to create
- * @param boolean $isTemplate Flag whether we create usual dictionary or dictionary template
- * @param string $template The existing FTS dictionary to be used as template for the new one
- * @param string $lexize The name of the function, which does transformation of input word
- * @param string $init The name of the function, which initializes dictionary
- * @param string $option Usually, it stores various options required for the dictionary
- * @param string $comment If omitted, defaults to nothing
- * @return 0 success
- */
- function createFtsDictionary($dictname, $isTemplate = false, $template = '', $lexize = '', $init = '', $option = '', $comment = '') {
- $this->fieldClean($dictname);
- $this->fieldClean($template);
- $this->fieldClean($lexize);
- $this->fieldClean($init);
- $this->fieldClean($option);
- $this->clean($comment);
-
- $sql = "CREATE TEXT SEARCH";
- if ($isTemplate) {
- $sql .= " TEMPLATE {$dictname} (";
- if ($lexize != '') $sql .= " LEXIZE = {$lexize}";
- if ($init != '') $sql .= ", INIT = {$init}";
- $sql .= ")";
- $whatToComment = 'TEXT SEARCH TEMPLATE';
- } else {
- $sql .= " DICTIONARY {$dictname} (";
- if ($template != '') $sql .= " TEMPLATE = {$template}";
- if ($option != '') $sql .= ", {$option}";
- $sql .= ")";
- $whatToComment = 'TEXT SEARCH DICTIONARY';
- }
-
- if ($comment != '') {
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
- }
-
- // Create the FTS dictionary
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Set the comment
- if ($comment != '') {
- $status = $this->setComment($whatToComment, $dictname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- return $this->endTransaction();
- }
-
- /**
- * Alters FTS dictionary or dictionary template
- */
- function updateFtsDictionary($dictname, $comment, $name) {
- $this->fieldClean($dictname);
- $this->fieldClean($name);
- $this->clean($comment);
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- $status = $this->setComment('TEXT SEARCH DICTIONARY', $dictname, '', $comment);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
-
- // Only if the name has changed
- if ($name != $dictname) {
- $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$dictname}\" RENAME TO \"{$name}\"";
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- return $this->endTransaction();
- }
-
- /**
- * Return all information relating to a FTS dictionary
- * @param $ftsdict The name of the FTS dictionary
- * @return FTS dictionary information
- */
- function getFtsDictionaryByName($ftsdict) {
- $this->clean($ftsdict);
- $sql = "SELECT
- n.nspname as schema,
- d.dictname as name,
- ( SELECT COALESCE(nt.nspname, '(null)')::pg_catalog.text || '.' || t.tmplname FROM
- pg_catalog.pg_ts_template t
- LEFT JOIN pg_catalog.pg_namespace nt ON nt.oid = t.tmplnamespace
- WHERE d.dicttemplate = t.oid ) AS template,
- d.dictinitoption as init,
- pg_catalog.obj_description(d.oid, 'pg_ts_dict') as comment
- FROM pg_catalog.pg_ts_dict d
- LEFT JOIN pg_catalog.pg_namespace n ON n.oid = d.dictnamespace
- WHERE d.dictname = '{$ftsdict}'
- AND pg_catalog.pg_ts_dict_is_visible(d.oid)
- ORDER BY schema, name";
-
- return $this->selectSet($sql);
- }
-
- /**
- * Creates/updates/deletes FTS mapping.
- * @param string $cfgname The name of the FTS configuration to alter
- * @param array $mapping Array of tokens' names
- * @param string $action What to do with the mapping: add, alter or drop
- * @param string $dictname Dictionary that will process tokens given or null in case of drop action
- * @return 0 success
- */
- function changeFtsMapping($ftscfg, $mapping, $action, $dictname = null) {
- $this->fieldClean($ftscfg);
- $this->fieldClean($dictname);
- $this->arrayClean($mapping);
-
- if (count($mapping) > 0) {
- switch ($action) {
- case 'alter':
- $whatToDo = "ALTER";
- break;
- case 'drop':
- $whatToDo = "DROP";
- break;
- default:
- $whatToDo = "ADD";
- break;
- }
- $sql = "ALTER TEXT SEARCH CONFIGURATION \"{$ftscfg}\" {$whatToDo} MAPPING FOR ";
- $sql .= implode(",", $mapping);
- if ($action != 'drop' && !empty($dictname)) {
- $sql .= " WITH {$dictname}";
- }
-
- $status = $this->beginTransaction();
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- $status = $this->execute($sql);
- if ($status != 0) {
- $this->rollbackTransaction();
- return -1;
- }
- return $this->endTransaction();
- } else {
- return -1;
- }
- }
-
- /**
- * Return all information related to a given FTS configuration's mapping
- * @param $ftscfg The name of the FTS configuration
- * @param $mapping The name of the mapping
- * @return FTS configuration information
- */
- function getFtsMappingByName($ftscfg, $mapping) {
- $this->fieldClean($ftscfg);
- $this->fieldClean($mapping);
-
- $getOidSql = "SELECT oid, cfgparser FROM pg_catalog.pg_ts_config WHERE cfgname = '{$ftscfg}'";
- $oidSet = $this->selectSet($getOidSql);
- $oid = $oidSet->fields['oid'];
- $cfgparser = $oidSet->fields['cfgparser'];
-
- $getTokenIdSql = "SELECT tokid FROM pg_catalog.ts_token_type({$cfgparser}) WHERE alias = '{$mapping}'";
- $tokenIdSet = $this->selectSet($getTokenIdSql);
- $tokid = $tokenIdSet->fields['tokid'];
-
- $sql = "SELECT
- (SELECT t.alias FROM pg_catalog.ts_token_type(c.cfgparser) AS t WHERE t.tokid = m.maptokentype) AS name,
- d.dictname as dictionaries
- FROM pg_catalog.pg_ts_config AS c, pg_catalog.pg_ts_config_map AS m, pg_catalog.pg_ts_dict d
- WHERE c.oid = {$oid} AND m.mapcfg = c.oid AND m.maptokentype = {$tokid} AND m.mapdict = d.oid
- LIMIT 1;";
- return $this->selectSet($sql);
- }
-
- /**
- * Return list of FTS mappings possible for given parser (specified by given configuration since configuration
- * can only have 1 parser)
- */
- function getFtsMappings($ftscfg) {
- $cfg = $this->getFtsConfigurationByName($ftscfg);
- $sql = "SELECT alias AS name, description FROM pg_catalog.ts_token_type({$cfg->fields['parser_id']}) ORDER BY name";
- return $this->selectSet($sql);
- }
-
- // Type functions
-
- /**
- * Creates a new enum type in the database
- * @param $name The name of the type
- * @param $values An array of values
- * @param $typcomment Type comment
- * @return 0 success
- * @return -1 transaction error
- * @return -2 no values supplied
- */
- function createEnumType($name, $values, $typcomment) {
- $this->fieldClean($name);
- $this->clean($typcomment);
-
- if (empty($values)) return -2;
-
- $status = $this->beginTransaction();
- if ($status != 0) return -1;
-
- $values = array_unique($values);
-
- $nbval = count($values);
-
- for ($i = 0; $i < $nbval; $i++)
- $this->clean($values[$i]);
-
- $sql = "CREATE TYPE \"{$this->_schema}\".\"{$name}\" AS ENUM ('";
- $sql.= implode("','", $values);
- $sql .= "')";
-
- $status = $this->execute($sql);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
-
- if ($typcomment != '') {
- $status = $this->setComment('TYPE', $name, '', $typcomment, true);
- if ($status) {
- $this->rollbackTransaction();
- return -1;
- }
- }
-
- return $this->endTransaction();
-
- }
-
- /**
- * Get defined values for a given enum
- * @return A recordset
- */
- function getEnumValues($name) {
- $this->fieldClean($name);
-
- $sql = "SELECT enumlabel AS enumval
- FROM pg_catalog.pg_type t JOIN pg_catalog.pg_enum e ON (t.oid=e.enumtypid)
- WHERE t.typname = '{$name}' ORDER BY e.oid";
- return $this->selectSet($sql);
- }
-
- // Function methods
-
- /**
- * Creates a new function.
- * @param $funcname The name of the function to create
- * @param $args A comma separated string of types
- * @param $returns The return type
- * @param $definition The definition for the new function
- * @param $language The language the function is written for
- * @param $flags An array of optional flags
- * @param $setof True if it returns a set, false otherwise
- * @param $rows number of rows planner should estimate will be returned
- * @param $cost cost the planner should use in the function execution step
- * @param $replace (optional) True if OR REPLACE, false for normal
- * @return 0 success
- */
- function createFunction($funcname, $args, $returns, $definition, $language, $flags, $setof, $cost, $rows, $replace = false) {
- $this->fieldClean($funcname);
- $this->clean($args);
- $this->clean($language);
- $this->arrayClean($flags);
- $this->clean($cost);
- $this->clean($rows);
-
- $sql = "CREATE";
- if ($replace) $sql .= " OR REPLACE";
- $sql .= " FUNCTION \"{$this->_schema}\".\"{$funcname}\" (";
-
- if ($args != '')
- $sql .= $args;
-
- // For some reason, the returns field cannot have quotes...
- $sql .= ") RETURNS ";
- if ($setof) $sql .= "SETOF ";
- $sql .= "{$returns} AS ";
-
- if (is_array($definition)) {
- $this->arrayClean($definition);
- $sql .= "'" . $definition[0] . "'";
- if ($definition[1]) {
- $sql .= ",'" . $definition[1] . "'";
- }
- } else {
- $this->clean($definition);
- $sql .= "'" . $definition . "'";
- }
-
- $sql .= " LANGUAGE \"{$language}\"";
-
- // Add costs
- if (!empty($cost))
- $sql .= " COST {$cost}";
-
- if ($rows <> 0 ){
- $sql .= " ROWS {$rows}";
- }
-
- // Add flags
- foreach ($flags as $v) {
- // Skip default flags
- if ($v == '') continue;
- else $sql .= "\n{$v}";
- }
-
- return $this->execute($sql);
- }
-
- /**
- * Returns all details for a particular function
- * @param $func The name of the function to retrieve
- * @return Function info
- */
- function getFunction($function_oid) {
- $this->clean($function_oid);
-
- $sql = "SELECT
- pc.oid AS prooid,
- proname,
- pg_catalog.pg_get_userbyid(proowner) AS proowner,
- nspname as proschema,
- lanname as prolanguage,
- procost,
- prorows,
- pg_catalog.format_type(prorettype, NULL) as proresult,
- prosrc,
- probin,
- proretset,
- proisstrict,
- provolatile,
- prosecdef,
- pg_catalog.oidvectortypes(pc.proargtypes) AS proarguments,
- proargnames AS proargnames,
- pg_catalog.obj_description(pc.oid, 'pg_proc') AS procomment,
- proconfig
- FROM
- pg_catalog.pg_proc pc, pg_catalog.pg_language pl, pg_catalog.pg_namespace pn
- WHERE
- pc.oid = '{$function_oid}'::oid
- AND pc.prolang = pl.oid
- AND pc.pronamespace = pn.oid
- ";
-
- return $this->selectSet($sql);
- }
-
- // Trigger functions
-
- /**
- * Grabs a list of triggers on a table
- * @param $table The name of a table whose triggers to retrieve
- * @return A recordset
- */
- function getTriggers($table = '') {
- $this->clean($table);
-
- $sql = "SELECT
- t.tgname, pg_catalog.pg_get_triggerdef(t.oid) AS tgdef,
- CASE WHEN t.tgenabled = 'D' THEN FALSE ELSE TRUE END AS tgenabled, p.oid AS prooid,
- p.proname || ' (' || pg_catalog.oidvectortypes(p.proargtypes) || ')' AS proproto,
- ns.nspname AS pronamespace
- FROM pg_catalog.pg_trigger t, pg_catalog.pg_proc p, pg_catalog.pg_namespace ns
- WHERE t.tgrelid = (SELECT oid FROM pg_catalog.pg_class WHERE relname='{$table}'
- AND relnamespace=(SELECT oid FROM pg_catalog.pg_namespace WHERE nspname='{$this->_schema}'))
- AND (NOT tgisconstraint OR NOT EXISTS
- (SELECT 1 FROM pg_catalog.pg_depend d JOIN pg_catalog.pg_constraint c
- ON (d.refclassid = c.tableoid AND d.refobjid = c.oid)
- WHERE d.classid = t.tableoid AND d.objid = t.oid AND d.deptype = 'i' AND c.contype = 'f'))
- AND p.oid=t.tgfoid
- AND p.pronamespace = ns.oid";
-
- return $this->selectSet($sql);
- }
-
-
-
- // Capabilities
- function hasCreateTableLikeWithIndexes() {return true;}
- function hasVirtualTransactionId() {return true;}
- function hasEnumTypes() {return true;}
- function hasFTS() {return true;}
- function hasFunctionCosting() {return true;}
- function hasFunctionGUC() {return true;}
-}
-?>
diff --git a/libraries/lib.inc.php b/libraries/lib.inc.php
index aa0825fc..acfcc722 100644
--- a/libraries/lib.inc.php
+++ b/libraries/lib.inc.php
@@ -20,7 +20,7 @@
// PostgreSQL and PHP minimum version
$postgresqlMinVer = '7.0';
- $phpMinVer = '4.1';
+ $phpMinVer = '5.0';
// Check the version of PHP
if (version_compare(phpversion(), $phpMinVer, '<'))
diff --git a/selenium/tests/config.inc.php b/selenium/tests/config.inc.php
index 38fe0d25..17a7d12c 100644
--- a/selenium/tests/config.inc.php
+++ b/selenium/tests/config.inc.php
@@ -1,7 +1,7 @@
<?php
require('../../lang/recoded/english.php');
-$webUrl = 'http://boox/~ioguix/ppa/ppa.test';
+$webUrl = 'http://boox/~ioguix/ppa.git/phppgadmin';
$serverName = '8.3'; // one of your $conf['servers'][*]['desc'] in conf/config.inc/php
#deprecated $superuser = 'ppatests_super'; // according to your selenium/tests/data/config.sql
#deprecated $superpass = 'super'; // according to your selenium/tests/data/config.sql