!C99Shell v. 2.0 [PHP 7 Update] [25.02.2019]!

Software: Apache/2.2.22 (Debian). PHP/5.6.36 

uname -a: Linux h05.hvosting.ua 4.9.110-amd64 #3 SMP Sun Nov 4 16:27:09 UTC 2018 x86_64 

uid=1389(h33678) gid=1099(h33678) groups=1099(h33678),502(mgrsecure) 

Safe-mode: OFF (not secure)

/usr/share/phppgadmin/classes/database/   drwxr-xr-x
Free 1.42 GB of 7.22 GB (19.68%)
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     Postgres.php (237.48 KB)      -rw-r--r--
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php

/**
 * 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 $
 */

include_once('./classes/database/ADODB_base.php');

class 
Postgres extends ADODB_base {

    var 
$major_version 9.0;
    
// 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(
        
'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'
    
);
    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',
        
'plpgsql' => 'SQL',
        
'php' => 'PHP',
        
'phpu' => 'PHP',
        
'plphp' => 'PHP',
        
'plphpu' => 'PHP',
        
'perl' => 'Perl',
        
'perlu' => 'Perl',
        
'plperl' => 'Perl',
        
'plperlu' => 'Perl',
        
'java' => 'Java',
        
'javau' => 'Java',
        
'pljava' => 'Java',
        
'pljavau' => 'Java',
        
'plj' => 'Java',
        
'plju' => 'Java',
        
'python' => 'Python',
        
'pythonu' => 'Python',
        
'plpython' => 'Python',
        
'plpythonu' => 'Python',
        
'ruby' => 'Ruby',
        
'rubyu' => 'Ruby',
        
'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''DELETE''REFERENCES''TRIGGER''ALL PRIVILEGES'),
          
'view' => array('SELECT''INSERT''UPDATE''DELETE''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'),
        
'column' => array('SELECT''INSERT''UPDATE''REFERENCES','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',
        
'D' => 'TRUNCATE',
          
'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
    
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
     * @param $conn The database connection
     */
    
function Postgres($conn) {
        
$this->ADODB_base($conn);
    }

    
// Formatting functions

    /**
     * Cleans (escapes) a string
     * @param $str The string to clean, by reference
     * @return The cleaned string
     */
    
function clean(&$str) {
        if (
$str === null) return null;
        
$str str_replace("\r\n","\n",$str);
        if (
function_exists('pg_escape_string'))
            
$str pg_escape_string($str);
        else
            
$str addslashes($str);
        return 
$str;
    }

    
/**
     * Cleans (escapes) an object name (eg. table, field)
     * @param $str The string to clean, by reference
     * @return The cleaned string
     */
    
function fieldClean(&$str) {
        if (
$str === null) return null;
        
$str str_replace('"''""'$str);
        return 
$str;
    }

    
/**
     * Cleans (escapes) an array of field names
     * @param $arr The array to clean, by reference
     * @return The cleaned array
     */
    
function fieldArrayClean(&$arr) {
        foreach (
$arr as $k => $v) {
            if (
$v === null) continue;
            
$arr[$k] = str_replace('"''""'$v);
        }
        return 
$arr;
    }

    
/**
     * Cleans (escapes) an array
     * @param $arr The array to clean, by reference
     * @return The cleaned array
     */
    
function arrayClean(&$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);
        }
        return 
$arr;
    }

    
/**
     * Escapes bytea data for display on the screen
     * @param $data The bytea data
     * @return Data formatted for on-screen display
     */
    
function escapeBytea($data) {
        if (
function_exists('pg_escape_bytea'))
            return 
stripslashes(pg_escape_bytea($data));
        else {
                 
$translations = array('\\a' => '\\007''\\b' => '\\010''\\t' => '\\011''\\n' => '\\012''\\v' => '\\013''\\f' => '\\014''\\r' => '\\015');
                 return 
strtr(addCSlashes($data"\0..\37\177..\377"), $translations);
        }
    }

    
/**
     * Outputs the HTML code for a particular field
     * @param $name The name to give the field
     * @param $value The value of the field.  Note this could be 'numeric(7,2)' sort of thing...
     * @param $type The database type of the field
     * @param $extras An array of attributes name as key and attributes' values as value
     */
    
function printField($name$value$type$extras = array()) {
        global 
$lang;

        
// Determine actions string
        
$extra_str '';
        foreach (
$extras as $k => $v) {
            
$extra_str .= {$k}=\"" htmlspecialchars($v) . "\"";
        }

        switch (
substr($type,0,9)) {
            case 
'bool':
            case 
'boolean':
                if (
$value !== null && $value == ''$value null;
                elseif (
$value == 'true'$value 't';
                elseif (
$value == 'false'$value 'f';

                
// If value is null, 't' or 'f'...
                
if ($value === null || $value == 't' || $value == 'f') {
                    echo 
"<select name=\""htmlspecialchars($name), "\"{$extra_str}>\n";
                    echo 
"<option value=\"\"", ($value === null) ? ' selected="selected"' ''"></option>\n";
                    echo 
"<option value=\"t\"", ($value == 't') ? ' selected="selected"' ''">{$lang['strtrue']}</option>\n";
                    echo 
"<option value=\"f\"", ($value == 'f') ? ' selected="selected"' ''">{$lang['strfalse']}</option>\n";
                    echo 
"</select>\n";
                }
                else {
                    echo 
"<input name=\""htmlspecialchars($name), "\" value=\""htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
                }
                break;
            case 
'bytea':
            case 
'bytea[]':
                
$value $this->escapeBytea($value);
            case 
'text':
            case 
'text[]':
            case 
'xml':
            case 
'xml[]':
                
$n substr_count($value"\n");
                
$n $n $n;
                
$n $n 20 20 $n;
                echo 
"<textarea name=\""htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"75\"{$extra_str}>\n";
                echo 
htmlspecialchars($value);
                echo 
"</textarea>\n";
                break;
            case 
'character':
            case 
'character[]':
                
$n substr_count($value"\n");
                
$n $n $n;
                
$n $n 20 20 $n;
                echo 
"<textarea name=\""htmlspecialchars($name), "\" rows=\"{$n}\" cols=\"35\"{$extra_str}>\n";
                echo 
htmlspecialchars($value);
                echo 
"</textarea>\n";
                break;
            default:
                echo 
"<input name=\""htmlspecialchars($name), "\" value=\""htmlspecialchars($value), "\" size=\"35\"{$extra_str} />\n";
                break;
        }
    }

    
/**
     * Formats a value or expression for sql purposes
     * @param $type The type of the field
     * @param $format VALUE or EXPRESSION
     * @param $value The actual value entered in the field.  Can be NULL
     * @return The suitably quoted and escaped value.
     */
    
function formatValue($type$format$value) {
        switch (
$type) {
            case 
'bool':
            case 
'boolean':
                if (
$value == 't')
                    return 
'TRUE';
                elseif (
$value == 'f')
                    return 
'FALSE';
                elseif (
$value == '')
                    return 
'NULL';
                else
                    return 
$value;
                break;
            default:
                
// Checking variable fields is difficult as there might be a size
                // attribute...
                
if (strpos($type'time') === 0) {
                    
// Assume it's one of the time types...
                    
if ($value == '') return "''";
                    elseif (
strcasecmp($value'CURRENT_TIMESTAMP') == 0
                            
|| strcasecmp($value'CURRENT_TIME') == 0
                            
|| strcasecmp($value'CURRENT_DATE') == 0
                            
|| strcasecmp($value'LOCALTIME') == 0
                            
|| strcasecmp($value'LOCALTIMESTAMP') == 0) {
                        return 
$value;
                    }
                    elseif (
$format == 'EXPRESSION')
                        return 
$value;
                    else {
                        
$this->clean($value);
                        return 
"'{$value}'";
                    }
                }
                else {
                    if (
$format == 'VALUE') {
                        
$this->clean($value);
                        return 
"'{$value}'";
                    }
                    return 
$value;
                }
        }
    }

    
/**
     * Formats a type correctly for display.  Postgres 7.0 had no 'format_type'
     * built-in function, and hence we need to do it manually.
     * @param $typname The name of the type
     * @param $typmod The contents of the typmod field
     */
    
function formatType($typname$typmod) {
        
// This is a specific constant in the 7.0 source
        
$varhdrsz 4;

        
// If the first character is an underscore, it's an array type
        
$is_array false;
        if (
substr($typname01) == '_') {
            
$is_array true;
            
$typname substr($typname1);
        }

        
// Show lengths on bpchar and varchar
        
if ($typname == 'bpchar') {
            
$len $typmod $varhdrsz;
            
$temp 'character';
            if (
$len 1)
                
$temp .= "({$len})";
        }
        elseif (
$typname == 'varchar') {
            
$temp 'character varying';
            if (
$typmod != -1)
                
$temp .= "(" . ($typmod $varhdrsz) . ")";
        }
        elseif (
$typname == 'numeric') {
            
$temp 'numeric';
            if (
$typmod != -1) {
                
$tmp_typmod $typmod $varhdrsz;
                
$precision = ($tmp_typmod >> 16) & 0xffff;
                
$scale $tmp_typmod 0xffff;
                
$temp .= "({$precision}{$scale})";
            }
        }
        else 
$temp $typname;

        
// Add array qualifier if it's an array
        
if ($is_array$temp .= '[]';

        return 
$temp;
    }

    
// 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;
    }

    function 
getHelpPages() {
        include_once(
'./help/PostgresDoc90.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
     * @param $currentdatabase database name that should be on top of the resultset
     * 
     * @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 pr.rolname='{$username}'";
        }
        else 
$clause '';

        if (
$currentdatabase != NULL) {
            
$this->clean($currentdatabase);
            
$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, 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,
                CASE WHEN pg_catalog.has_database_privilege(current_user, pdb.oid, 'CONNECT') 
                    THEN pg_catalog.pg_database_size(pdb.oid) 
                    ELSE -1 -- set this magic value, which we will convert to no access later  
                END as dbsize, pdb.datcollate, pdb.datctype
            FROM pg_catalog.pg_database pdb
                LEFT JOIN pg_catalog.pg_roles pr ON (pdb.datdba = pr.oid)
            WHERE true
                
{$where}
                
{$clause}
            
{$orderby}";

        return 
$this->selectSet($sql);
    }

    
/**
     * 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 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);
    }

    
/**
     * 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 getDatabaseOwner($database) {
        
$this->clean($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);
    }

    
/**
     * Returns the current database encoding
     * @return The encoding.  eg. SQL_ASCII, UTF-8, etc.
     */
    
function getDatabaseEncoding() {
        
// Try to avoid a query if at all possible (5)
        
if (function_exists('pg_parameter_status')) {
            
$encoding pg_parameter_status($this->conn->_connectionID'server_encoding');
            if (
$encoding !== false) return $encoding;
        }

        
$sql "SELECT getdatabaseencoding() AS encoding";

        return 
$this->selectField($sql'encoding');
    }

    
/**
     * 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');
    }

    
/**
     * Creates a database
     * @param $database The name of the database to create
     * @param $encoding Encoding of the database
     * @param $tablespace (optional) The tablespace name
     * @return 0 success
     * @return -1 tablespace error
     * @return -2 comment error
     */
    
function createDatabase($database$encoding$tablespace ''$comment ''$template 'template1',
        
$lc_collate ''$lc_ctype '')
    {
        
$this->fieldClean($database);
        
$this->clean($encoding);
        
$this->fieldClean($tablespace);
        
$this->fieldClean($template);
        
$this->clean($lc_collate);
        
$this->clean($lc_ctype);

        
$sql "CREATE DATABASE \"{$database}\" WITH TEMPLATE=\"{$template}\"";

        if (
$encoding != ''$sql .= " ENCODING='{$encoding}'";
        if (
$lc_collate != ''$sql .= " LC_COLLATE='{$lc_collate}'";
        if (
$lc_ctype != ''$sql .= " LC_CTYPE='{$lc_ctype}'";

        if (
$tablespace != '' && $this->hasTablespaces()) $sql .= " TABLESPACE \"{$tablespace}\"";

        
$status $this->execute($sql);
        if (
$status != 0) return -1;

        if (
$comment != '' && $this->hasSharedComments()) {
            
$status $this->setComment('DATABASE',$database,'',$comment);
            if (
$status != 0) return -2;
        }

        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->fieldClean($oldName);
        
$this->fieldClean($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;
    }

    
/**
     * Drops a database
     * @param $database The name of the database to drop
     * @return 0 success
     */
    
function dropDatabase($database) {
        
$this->fieldClean($database);
        
$sql "DROP DATABASE \"{$database}\"";
        return 
$this->execute($sql);
    }

    
/**
     * 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->fieldClean($dbName);
        
$this->fieldClean($newOwner);

        
$sql "ALTER DATABASE \"{$dbName}\" OWNER TO \"{$newOwner}\"";
        return 
$this->execute($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
     * @return -4 comment error
     */
    
function alterDatabase($dbName$newName$newOwner ''$comment '') {

        
$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;
            }
            
$dbName $newName;
        }

        if (
$newOwner != '') {
            
$status $this->alterDatabaseOwner($newName$newOwner);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
2;
            }
        }
        
        
$this->fieldClean($dbName);
        
$status $this->setComment('DATABASE'$dbName''$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }
        return 
$this->endTransaction();
    }

    
/**
     * 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);
    }

    
/**
     * 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;

        
/*about escaping:
         * SET standard_conforming_string is not available before 8.2
         * So we must use PostgreSQL specific notation :/
         * E'' notation is not available before 8.1
         * $$ is available since 8.0
         * Nothing specific from 7.4
         **/

        // Escape search term for ILIKE match
        
$this->clean($term);
        
$this->clean($filter);
        
$term str_replace('_''\_'$term);
        
$term str_replace('%''\%'$term);

        
// 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 \$_PATERN_\$pg\_%\$_PATERN_\$ AND pn.nspname != 'information_schema'";
            
$lan_where "AND pl.lanispl";
        }
        else {
            
$where '';
            
$lan_where '';
        }

        
// Apply outer filter
        
$sql '';
        if (
$filter != '') {
            
$sql "SELECT * FROM (";
        }

        
$term "\$_PATERN_\$%{$term}%\$_PATERN_\$";

        
$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 ( pt.tgconstraint = 0 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}%' ";
        }

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

        return 
$this->selectSet($sql);
    }

    
/**
     * Returns all available variable information.
     * @return A recordset
     */
    
function getVariables() {
        
$sql "SHOW ALL";

        return 
$this->selectSet($sql);
    }

    
// Schema functons

    /**
     * Return all schemas in the current database.
     * @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.rolname AS nspowner,
                pg_catalog.obj_description(pn.oid, 'pg_namespace') AS nspcomment
            FROM pg_catalog.pg_namespace pn
                LEFT JOIN pg_catalog.pg_roles pu ON (pn.nspowner = pu.oid)
            
{$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, r.rolname AS ownername, nspacl,
                pg_catalog.obj_description(pn.oid, 'pg_namespace') as nspcomment
            FROM pg_catalog.pg_namespace pn
                LEFT JOIN pg_roles as r ON pn.nspowner = r.oid
            WHERE nspname='
{$schema}'";
        return 
$this->selectSet($sql);
    }

    
/**
     * 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->_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) == && $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);
         }

    
/**
     * 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);

        
$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;
    }

    
/**
     * Updates a schema.
     * @param $schemaname The name of the schema to drop
     * @param $comment The new comment for this schema
     * @param $owner The new owner for this schema
     * @return 0 success
     */
    
function updateSchema($schemaname$comment$name$owner) {
        
$this->fieldClean($schemaname);
        
$this->fieldClean($name);
        
$this->fieldClean($owner);

        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
$status $this->setComment('SCHEMA'$schemaname''$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
$schema_rs $this->getSchemaByName($schemaname);
        
/* Only if the owner change */
        
if ($schema_rs->fields['ownername'] != $owner) {
            
$sql "ALTER SCHEMA \"{$schemaname}\" OWNER TO \"{$owner}\"";
            
$status $this->execute($sql);
            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();
    }

    
/**
     * 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);
        }

    
/**
     * 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'));
        }

    
// 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 null error
     **/
    
function hasObjectID($table) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_schema}')";

        
$rs $this->selectSet($sql);
        if (
$rs->recordCount() != 1) return null;
        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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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 = '
{$c_schema}'
                  AND n.oid = c.relnamespace
                  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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        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='
{$c_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 '') {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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, a.attnum,
                    pg_catalog.format_type(a.atttypid, a.atttypmod) as type,
                    a.atttypmod,
                    a.attnotnull, a.atthasdef, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as 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 = '
{$c_schema}'))
                    AND a.attnum > 0 AND NOT a.attisdropped
                ORDER BY a.attnum"
;
        }
        else {
            
$sql "
                SELECT
                    a.attname, a.attnum,
                    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, pg_catalog.pg_get_expr(adef.adbin, adef.adrelid, true) as 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 = '
{$c_schema}'))
                    AND a.attname = '
{$field}'";
        }

        return 
$this->selectSet($sql);
    }

    
/**
     * 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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 = '
{$c_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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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 = '
{$c_schema}'))
        "
;

        return 
$this->selectSet($sql);
    }

    
/**
     * Returns the SQL definition for the table.
     * @pre MUST be run within a transaction
     * @param $table The table to define
     * @param $clean True to issue drop command, false otherwise
     * @return A string containing the formatted SQL code
     * @return null On error
     */
    
function getTableDefPrefix($table$clean false) {
        
// Fetch table
        
$t $this->getTable($table);
        if (!
is_object($t) || $t->recordCount() != 1) {
            
$this->rollbackTransaction();
            return 
null;
        }
        
$this->fieldClean($t->fields['relname']);
        
$this->fieldClean($t->fields['nspname']);

        
// Fetch attributes
        
$atts $this->getTableAttributes($table);
        if (!
is_object($atts)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        
// Fetch constraints
        
$cons $this->getConstraints($table);
        if (!
is_object($cons)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        
// Output a reconnect command to create the table as the correct user
        
$sql $this->getChangeUserSQL($t->fields['relowner']) . "\n\n";

        
// Set schema search path
        
$sql .= "SET search_path = \"{$t->fields['nspname']}\", pg_catalog;\n\n";

        
// Begin CREATE TABLE definition
        
$sql .= "-- Definition\n\n";
        
// DROP TABLE must be fully qualified in case a table with the same name exists
        // in pg_catalog.
        
if (!$clean$sql .= "-- ";
        
$sql .= "DROP TABLE ";
        
$sql .= "\"{$t->fields['nspname']}\".\"{$t->fields['relname']}\";\n";
        
$sql .= "CREATE TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" (\n";

        
// Output all table columns
        
$col_comments_sql '';   // Accumulate comments on columns
        
$num $atts->recordCount() + $cons->recordCount();
        
$i 1;
        while (!
$atts->EOF) {
            
$this->fieldClean($atts->fields['attname']);
            
$sql .= "    \"{$atts->fields['attname']}\"";
            
// Dump SERIAL and BIGSERIAL columns correctly
            
if ($this->phpBool($atts->fields['attisserial']) &&
                    (
$atts->fields['type'] == 'integer' || $atts->fields['type'] == 'bigint')) {
                if (
$atts->fields['type'] == 'integer')
                    
$sql .= " SERIAL";
                else
                    
$sql .= " BIGSERIAL";
            }
            else {
                
$sql .= " " $this->formatType($atts->fields['type'], $atts->fields['atttypmod']);

                
// Add NOT NULL if necessary
                
if ($this->phpBool($atts->fields['attnotnull']))
                    
$sql .= " NOT NULL";
                
// Add default if necessary
                
if ($atts->fields['adsrc'] !== null)
                    
$sql .= " DEFAULT {$atts->fields['adsrc']}";
            }

            
// Output comma or not
            
if ($i $num$sql .= ",\n";
            else 
$sql .= "\n";

            
// Does this column have a comment?
            
if ($atts->fields['comment'] !== null) {
                
$this->clean($atts->fields['comment']);
                
$col_comments_sql .= "COMMENT ON COLUMN \"{$t->fields['relname']}\".\"{$atts->fields['attname']}\"  IS '{$atts->fields['comment']}';\n";
            }

            
$atts->moveNext();
            
$i++;
        }
        
// Output all table constraints
        
while (!$cons->EOF) {
            
$this->fieldClean($cons->fields['conname']);
            
$sql .= "    CONSTRAINT \"{$cons->fields['conname']}\" ";
            
// Nasty hack to support pre-7.4 PostgreSQL
            
if ($cons->fields['consrc'] !== null)
                
$sql .= $cons->fields['consrc'];
            else {
                switch (
$cons->fields['contype']) {
                    case 
'p':
                        
$keys $this->getAttributeNames($tableexplode(' '$cons->fields['indkey']));
                        
$sql .= "PRIMARY KEY (" join(','$keys) . ")";
                        break;
                    case 
'u':
                        
$keys $this->getAttributeNames($tableexplode(' '$cons->fields['indkey']));
                        
$sql .= "UNIQUE (" join(','$keys) . ")";
                        break;
                    default:
                        
// Unrecognised constraint
                        
$this->rollbackTransaction();
                        return 
null;
                }
            }

            
// Output comma or not
            
if ($i $num$sql .= ",\n";
            else 
$sql .= "\n";

            
$cons->moveNext();
            
$i++;
        }

        
$sql .= ")";

        
// @@@@ DUMP CLUSTERING INFORMATION

        // Inherits
        /*
         * XXX: This is currently commented out as handling inheritance isn't this simple.
         * You also need to make sure you don't dump inherited columns and defaults, as well
         * as inherited NOT NULL and CHECK constraints.  So for the time being, we just do
         * not claim to support inheritance.
        $parents = $this->getTableParents($table);
        if ($parents->recordCount() > 0) {
            $sql .= " INHERITS (";
            while (!$parents->EOF) {
                $this->fieldClean($parents->fields['relname']);
                // Qualify the parent table if it's in another schema
                if ($parents->fields['schemaname'] != $this->_schema) {
                    $this->fieldClean($parents->fields['schemaname']);
                    $sql .= "\"{$parents->fields['schemaname']}\".";
                }
                $sql .= "\"{$parents->fields['relname']}\"";

                $parents->moveNext();
                if (!$parents->EOF) $sql .= ', ';
            }
            $sql .= ")";
        }
        */

        // Handle WITHOUT OIDS
        
if ($this->hasObjectID($table))
            
$sql .= " WITH OIDS";
        else
            
$sql .= " WITHOUT OIDS";

        
$sql .= ";\n";

        
// Column storage and statistics
        
$atts->moveFirst();
        
$first true;
        while (!
$atts->EOF) {
            
$this->fieldClean($atts->fields['attname']);
            
// Statistics first
            
if ($atts->fields['attstattarget'] >= 0) {
                if (
$first) {
                    
$sql .= "\n";
                    
$first false;
                }
                
$sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STATISTICS {$atts->fields['attstattarget']};\n";
            }
            
// Then storage
            
if ($atts->fields['attstorage'] != $atts->fields['typstorage']) {
                switch (
$atts->fields['attstorage']) {
                    case 
'p':
                        
$storage 'PLAIN';
                        break;
                    case 
'e':
                        
$storage 'EXTERNAL';
                        break;
                    case 
'm':
                        
$storage 'MAIN';
                        break;
                    case 
'x':
                        
$storage 'EXTENDED';
                        break;
                    default:
                        
// Unknown storage type
                        
$this->rollbackTransaction();
                        return 
null;
                }
                
$sql .= "ALTER TABLE ONLY \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" ALTER COLUMN \"{$atts->fields['attname']}\" SET STORAGE {$storage};\n";
            }

            
$atts->moveNext();
        }

        
// Comment
        
if ($t->fields['relcomment'] !== null) {
            
$this->clean($t->fields['relcomment']);
            
$sql .= "\n-- Comment\n\n";
            
$sql .= "COMMENT ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" IS '{$t->fields['relcomment']}';\n";
        }

        
// Add comments on columns, if any
        
if ($col_comments_sql != ''$sql .= $col_comments_sql;

        
// Privileges
        
$privs $this->getPrivileges($table'table');
        if (!
is_array($privs)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        if (
sizeof($privs) > 0) {
            
$sql .= "\n-- Privileges\n\n";
            
/*
             * Always start with REVOKE ALL FROM PUBLIC, so that we don't have to
             * wire-in knowledge about the default public privileges for different
             * kinds of objects.
             */
            
$sql .= "REVOKE ALL ON TABLE \"{$t->fields['nspname']}\".\"{$t->fields['relname']}\" FROM PUBLIC;\n";
            foreach (
$privs as $v) {
                
// Get non-GRANT OPTION privs
                
$nongrant array_diff($v[2], $v[4]);

                
// Skip empty or owner ACEs
                
if (sizeof($v[2]) == || ($v[0] == 'user' && $v[1] == $t->fields['relowner'])) continue;

                
// Change user if necessary
                
if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
                    
$grantor $v[3];
                    
$this->clean($grantor);
                    
$sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
                }

                
// Output privileges with no GRANT OPTION
                
$sql .= "GRANT " join(', '$nongrant) . " ON TABLE \"{$t->fields['relname']}\" TO ";
                switch (
$v[0]) {
                    case 
'public':
                        
$sql .= "PUBLIC;\n";
                        break;
                    case 
'user':
                        
$this->fieldClean($v[1]);
                        
$sql .= "\"{$v[1]}\";\n";
                        break;
                    case 
'group':
                        
$this->fieldClean($v[1]);
                        
$sql .= "GROUP \"{$v[1]}\";\n";
                        break;
                    default:
                        
// Unknown privilege type - fail
                        
$this->rollbackTransaction();
                        return 
null;
                }

                
// Reset user if necessary
                
if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
                    
$sql .= "RESET SESSION AUTHORIZATION;\n";
                }

                
// Output privileges with GRANT OPTION

                // Skip empty or owner ACEs
                
if (!$this->hasGrantOption() || sizeof($v[4]) == 0) continue;

                
// Change user if necessary
                
if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
                    
$grantor $v[3];
                    
$this->clean($grantor);
                    
$sql .= "SET SESSION AUTHORIZATION '{$grantor}';\n";
                }

                
$sql .= "GRANT " join(', '$v[4]) . " ON \"{$t->fields['relname']}\" TO ";
                switch (
$v[0]) {
                    case 
'public':
                        
$sql .= "PUBLIC";
                        break;
                    case 
'user':
                        
$this->fieldClean($v[1]);
                        
$sql .= "\"{$v[1]}\"";
                        break;
                    case 
'group':
                        
$this->fieldClean($v[1]);
                        
$sql .= "GROUP \"{$v[1]}\"";
                        break;
                    default:
                        
// Unknown privilege type - fail
                        
return null;
                }
                
$sql .= " WITH GRANT OPTION;\n";

                
// Reset user if necessary
                
if ($this->hasGrantOption() && $v[3] != $t->fields['relowner']) {
                    
$sql .= "RESET SESSION AUTHORIZATION;\n";
                }

            }
        }

        
// Add a newline to separate data that follows (if any)
        
$sql .= "\n";

        return 
$sql;
    }

    
/**
     * Returns extra table definition information that is most usefully
     * dumped after the table contents for speed and efficiency reasons
     * @param $table The table to define
     * @return A string containing the formatted SQL code
     * @return null On error
     */
    
function getTableDefSuffix($table) {
        
$sql '';

        
// Indexes
        
$indexes $this->getIndexes($table);
        if (!
is_object($indexes)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        if (
$indexes->recordCount() > 0) {
            
$sql .= "\n-- Indexes\n\n";
            while (!
$indexes->EOF) {
                
$sql .= $indexes->fields['inddef'] . ";\n";

                
$indexes->moveNext();
            }
        }

        
// Triggers
        
$triggers $this->getTriggers($table);
        if (!
is_object($triggers)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        if (
$triggers->recordCount() > 0) {
            
$sql .= "\n-- Triggers\n\n";
            while (!
$triggers->EOF) {
                
// Nasty hack to support pre-7.4 PostgreSQL
                
if ($triggers->fields['tgdef'] !== null)
                    
$sql .= $triggers->fields['tgdef'];
                else
                    
$sql .= $this->getTriggerDef($triggers->fields);

                
$sql .= ";\n";

                
$triggers->moveNext();
            }
        }

        
// Rules
        
$rules $this->getRules($table);
        if (!
is_object($rules)) {
            
$this->rollbackTransaction();
            return 
null;
        }

        if (
$rules->recordCount() > 0) {
            
$sql .= "\n-- Rules\n\n";
            while (!
$rules->EOF) {
                
$sql .= $rules->fields['definition'] . "\n";

                
$rules->moveNext();
            }
        }

        return 
$sql;
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);

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

        
$found false;
        
$first true;
        
$comment_sql ''//Accumulate comments for the columns
        
$sql "CREATE TABLE \"{$f_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 ($withoutoids)
            
$sql .= ' WITHOUT OIDS';
        else
            
$sql .= ' WITH 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$tblcommenttrue);
            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 '') {

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$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 \"{$f_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
     * /!\ this function is called from _alterTable which take care of escaping fields
     * @param $tblrs The table RecordSet returned by getTable()
     * @param $name The new table's name
     * @return 0 success
     */
    
function alterTableName($tblrs$name null) {
        
/* vars cleaned in _alterTable */
        // Rename (only if name has changed)
        
if (!empty($name) && ($name != $tblrs->fields['relname'])) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
            
$sql "ALTER TABLE \"{$f_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
     * /!\ this function is called from _alterTable which take care of escaping fields
     * @param $tblrs The table RecordSet returned by getTable()
     * @param $name The new table's owner
     * @return 0 success
     */
    
function alterTableOwner($tblrs$owner null) {
        
/* vars cleaned in _alterTable */
        
if (!empty($owner) && ($tblrs->fields['relowner'] != $owner)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
// 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 \"{$f_schema}\".\"{$tblrs->fields['relname']}\" OWNER TO \"{$owner}\"";

            return 
$this->execute($sql);
        }
        return 
0;
    }

    
/**
     * Alter a table's tablespace
     * /!\ this function is called from _alterTable which take care of escaping fields
     * @param $tblrs The table RecordSet returned by getTable()
     * @param $name The new table's tablespace
     * @return 0 success
     */
    
function alterTableTablespace($tblrs$tablespace null) {
        
/* vars cleaned in _alterTable */
        
if (!empty($tablespace) && ($tblrs->fields['tablespace'] != $tablespace)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
            
// If tablespace has been changed, then do the alteration.  We
            // don't want to do this unnecessarily.
            
$sql "ALTER TABLE \"{$f_schema}\".\"{$tblrs->fields['relname']}\" SET TABLESPACE \"{$tablespace}\"";

            return 
$this->execute($sql);
        }
        return 
0;
    }

    
/**
     * Alter a table's schema
     * /!\ this function is called from _alterTable which take care of escaping fields
     * @param $tblrs The table RecordSet returned by getTable()
     * @param $name The new table's schema
     * @return 0 success
     */
    
function alterTableSchema($tblrs$schema null) {
        
/* vars cleaned in _alterTable */
        
if (!empty($schema) && ($tblrs->fields['nspname'] != $schema)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
// If tablespace has been changed, then do the alteration.  We
            // don't want to do this unnecessarily.
            
$sql "ALTER TABLE \"{$f_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
        
$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) {

        
$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
     * 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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;
        }
    }

    
/**
     * Empties a table in the database
     * @param $table The table to be emptied
     * @return 0 success
     */
    
function emptyTable($table) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);

        
$sql "DELETE FROM \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);

        
$sql "DROP TABLE \"{$f_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
     * @param $type The type of the column
     * @param $array True if array type, false otherwise
     * @param $notnull True if NOT NULL, false otherwise
     * @param $default The default for the column.  '' for none.
     * @param $length The optional size of the column (ie. 30 for varchar(30))
     * @return 0 success
     */
    
function addColumn($table$column$type$array$length$notnull$default$comment) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);
        
$this->clean($type);
        
$this->clean($length);

        if (
$length == '')
            
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$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($type9);
                    
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" timestamp({$length}){$qual}";
                    break;
                case 
'time with time zone':
                case 
'time without time zone':
                    
$qual substr($type4);
                    
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}";
                    break;
                default:
                    
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD COLUMN \"{$column}\" {$type}({$length})";
            }
        }

        
// Add array qualifier, if requested
        
if ($array$sql .= '[]';

        
// If we have advanced column adding, add the extra qualifiers
        
if ($this->hasCreateFieldWithConstraints()) {
            
// NOT NULL clause
            
if ($notnull$sql .= ' NOT NULL';

            
// DEFAULT clause
            
if ($default != ''$sql .= ' DEFAULT ' $default;
        }

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

        
$status $this->execute($sql);
        if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
1;
            }

        
$status $this->setComment('COLUMN'$column$table$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
    }

        return 
$this->endTransaction();
    }

    
/**
     * 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 -4 rename column error
     * @return -5 comment error
     * @return -6 transaction error
     */
    
function alterColumn($table$column$name$notnull$oldnotnull$default$olddefault,
        
$type$length$array$oldtype$comment)
    {
        
// Begin transaction
        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
6;
        }

        
// Rename the column, if it has been changed
        
if ($column != $name) {
            
$status $this->renameColumn($table$column$name);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
4;
            }
        }

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$toAlter = array();
        
// Create the command for changing nullability
        
if ($notnull != $oldnotnull) {
            
$toAlter[] = "ALTER COLUMN \"{$name}\" ". (($notnull) ? 'SET' 'DROP') . " NOT NULL";
        }

        
// Add default, if it has changed
        
if ($default != $olddefault) {
            if (
$default == '') {
                
$toAlter[] = "ALTER COLUMN \"{$name}\" DROP DEFAULT";
            }
            else {
                
$toAlter[] = "ALTER COLUMN \"{$name}\" 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($type9);
                    
$ftype "timestamp({$length}){$qual}";
                    break;
                case 
'time with time zone':
                case 
'time without time zone':
                    
$qual substr($type4);
                    
$ftype "time({$length}){$qual}";
                    break;
                default:
                    
$ftype "{$type}({$length})";
            }
        }

        
// Add array qualifier, if requested
        
if ($array$ftype .= '[]';

        if (
$ftype != $oldtype) {
            
$toAlter[] = "ALTER COLUMN \"{$name}\" TYPE {$ftype}";
        }

        
// Attempt to process the batch alteration, if anything has been changed
        
if (!empty($toAlter)) {
            
// Initialise an empty SQL string
            
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" "
                
implode(','$toAlter);
    
            
$status $this->execute($sql);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        
// Update the comment on the column
        
$status $this->setComment('COLUMN'$name$table$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
5;
        }

        return 
$this->endTransaction();
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);
        
$this->fieldClean($newName);

        
$sql "ALTER TABLE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$sql "ALTER TABLE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);

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

        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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" DROP COLUMN \"{$column}\"";
        if (
$cascade$sql .= " CASCADE";

        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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";

        return 
$this->execute($sql);
    }

    
/**
     * 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;
        }
        
        
// Set extra_float_digits to 2
        
$sql "SET extra_float_digits TO 2";
        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }
        
        return 
0;
    }

    
/**
     * 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}\"");
    }
    
    
/**
     * Returns all available autovacuum per table information.
     * @param $table if given, return autovacuum info for the given table or return all informations for all table
     *   
     * @return A recordset
     */
    
function getTableAutovacuum($table='') {

        
$sql '';

        if (
$table !== '') {
            
$this->clean($table);
            
$c_schema $this->_schema;
            
$this->clean($c_schema);

            
$sql "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
                FROM pg_class c
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE c.relkind = 'r'::\"char\"
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
                    AND c.reloptions IS NOT NULL
                    AND c.relname = '
{$table}' AND n.nspname = '{$c_schema}'
                ORDER BY nspname, relname"
;
        }
        else {
            
$sql "SELECT c.oid, nspname, relname, pg_catalog.array_to_string(reloptions, E',') AS reloptions
                FROM pg_class c
                    LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
                WHERE c.relkind = 'r'::\"char\"
                    AND n.nspname NOT IN ('pg_catalog','information_schema')
                    AND c.reloptions IS NOT NULL
                ORDER BY nspname, relname"
;

        }

        
/* tmp var to parse the results */
        
$_autovacs $this->selectSet($sql);

        
/* result aray to return as RS */
        
$autovacs = array();
        while (!
$_autovacs->EOF) {
            
$_ = array(
                
'nspname' => $_autovacs->fields['nspname'],
                
'relname' => $_autovacs->fields['relname']
            );

            foreach (
explode(','$_autovacs->fields['reloptions']) AS $var) {
                list(
$o$v) = explode('='$var);
                
$_[$o] = $v
            }

            
$autovacs[] = $_;
            
            
$_autovacs->moveNext();
        }

        include_once(
'./classes/ArrayRecordSet.php');
        return new 
ArrayRecordSet($autovacs);
    }

    
// Row 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;
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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($oldtableexplode(' '$rs->fields['indkey']));
            if (!
is_array($attnames)) {
                
$this->rollbackTransaction();
                return -
1;
            }
            else {
                
$this->endTransaction();
                return 
$attnames;
            }
        }
    }

    
/**
     * Adds a new row to a table
     * @param $table The table in which to insert
     * @param $fields Array of given field in values
     * @param $values Array of 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$fields$values$nulls$format$types) {

        if (!
is_array($fields) || !is_array($values) || !is_array($nulls)
            || !
is_array($format) || !is_array($types)
            || (
count($fields) != count($values))
        ) {
            return -
1;
        }
        else {
            
// Build clause
            
if (count($values) > 0) {
                
// Escape all field names
                
$fields array_map(array('Postgres','fieldClean'), $fields);
                
$f_schema $this->_schema;
                
$this->fieldClean($table);
                
$this->fieldClean($f_schema);

                
$sql '';
                foreach(
$values as $i => $value) {

                    
// Handle NULL values
                    
if (isset($nulls[$i]))
                        
$sql .= ',NULL';
                    else
                        
$sql .= ',' $this->formatValue($types[$i], $format[$i], $value);
                }

                
$sql "INSERT INTO \"{$f_schema}\".\"{$table}\" (\""implode('","'$fields) ."\")
                    VALUES ("
substr($sql1) .")";

                return 
$this->execute($sql);
            }
        }

        return -
1;
    }

    
/**
     * Updates a row in a table
     * @param $table The table in which to update
     * @param $vars 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
     * @param $keyarr An array mapping column => value to update
     * @return 0 success
     * @return -1 invalid parameters
     */
    
function editRow($table$vars$nulls$format$types$keyarr) {
        if (!
is_array($vars) || !is_array($nulls) || !is_array($format) || !is_array($types))
            return -
1;
        else {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($table);

            
// Build clause
            
if (sizeof($vars) > 0) {

                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 (isset(
$sql)) $sql .= ", \"{$key}\"={$tmp}";
                    else 
$sql "UPDATE \"{$f_schema}\".\"{$table}\" SET \"{$key}\"={$tmp}";
                }
                
$first true;
                foreach (
$keyarr as $k => $v) {
                    
$this->fieldClean($k);
                    
$this->clean($v);
                    if (
$first) {
                        
$sql .= " WHERE \"{$k}\"='{$v}'";
                        
$first false;
                    }
                    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();
                return -
1;
        }

           
$status $this->execute($sql);
            if (
$status != 0) { // update failed
            
$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();
    }
    }

    
/**
     * 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 deleteRow($table$key$schema=false) {
        if (!
is_array($key)) return -1;
        else {
            
// 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;
            }
            
            if (
$schema === false$schema $this->_schema;

            
$status $this->delete($table$key$schema);
            if (
$status != || $this->conn->Affected_Rows() != 1) {
                
$this->rollbackTransaction();
                return -
2;
            }

            
// End transaction
            
return $this->endTransaction();
        }
    }

    
// Sequence functions

    /**
     * Returns properties of a single sequence
     * @param $sequence Sequence name
     * @return A recordset
     */
    
function getSequence($sequence) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$c_sequence $sequence;
        
$this->fieldClean($sequence);
        
$this->clean($c_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 = '
{$c_sequence}' AND c.relkind = 'S' AND n.nspname='{$c_schema}'
                AND n.oid = c.relnamespace"
;

        return 
$this->selectSet$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 {
            
$c_schema $this->_schema;
            
$this->clean($c_schema);
            
$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='
{$c_schema}' ORDER BY seqname";
        }

        return 
$this->selectSet$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 */
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->clean($f_schema);
        
$this->fieldClean($sequence);
        
$this->clean($sequence);

        
$sql "SELECT pg_catalog.NEXTVAL('\"{$f_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 */
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->clean($f_schema);
        
$this->fieldClean($sequence);
        
$this->clean($sequence);
        
$this->clean($nextvalue);

        
$sql "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', '{$nextvalue}')";

        return 
$this->execute($sql);
    }

    
/**
     * Restart a given sequence to its start value
     * @param $sequence Sequence name
     * @return 0 success
     * @return -1 sequence not found
     */
    
function restartSequence($sequence) {

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($sequence);

        
$sql "ALTER SEQUENCE \"{$f_schema}\".\"{$sequence}\" RESTART;";

        return 
$this->execute($sql);
    }

    
/**
     * 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'];

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
/* This double-cleaning is deliberate */
        
$this->fieldClean($sequence);
        
$this->clean($sequence);

        
$sql "SELECT pg_catalog.SETVAL('\"{$f_schema}\".\"{$sequence}\"', {$minvalue})";

        return 
$this->execute($sql);
    }

    
/**
     * Creates a new sequence
     * @param $sequence Sequence name
     * @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
     */
    
function createSequence($sequence$increment$minvalue$maxvalue,
                                
$startvalue$cachevalue$cycledvalue) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($sequence);
        
$this->clean($increment);
        
$this->clean($minvalue);
        
$this->clean($maxvalue);
        
$this->clean($startvalue);
        
$this->clean($cachevalue);

        
$sql "CREATE SEQUENCE \"{$f_schema}\".\"{$sequence}\"";
        if (
$increment != ''$sql .= " INCREMENT {$increment}";
        if (
$minvalue != ''$sql .= " MINVALUE {$minvalue}";
        if (
$maxvalue != ''$sql .= " MAXVALUE {$maxvalue}";
        if (
$startvalue != ''$sql .= " START {$startvalue}";
        if (
$cachevalue != ''$sql .= " CACHE {$cachevalue}";
        if (
$cycledvalue$sql .= " CYCLE";

        return 
$this->execute($sql);
    }

    
/**
     * Rename a sequence
     * @param $seqrs The sequence RecordSet returned by getSequence()
     * @param $name The new name for the sequence
     * @return 0 success
     */
    
function alterSequenceName($seqrs$name) {
        
/* vars are cleaned in _alterSequence */
        
if (!empty($name) && ($seqrs->fields['seqname'] != $name)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$sql "ALTER SEQUENCE \"{$f_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.
        /* vars are cleaned in _alterSequence */
        
if (!empty($owner) && ($seqrs->fields['seqowner'] != $owner)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$sql "ALTER SEQUENCE \"{$f_schema}\".\"{$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) {
        
/* vars are cleaned in _alterSequence */
        
if (!empty($schema) && ($seqrs->fields['nspname'] != $schema)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$sql "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" SET SCHEMA {$schema}";
            return 
$this->execute($sql);
        }
        return 
0;
    }

    
/**
     * 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 $restartvalue The sequence current value
     * @param $cachevalue The sequence cache value
     * @param $cycledvalue Sequence can cycle ?
     * @param $startvalue The sequence start value when issueing a restart
     * @return 0 success
     */
    
function alterSequenceProps($seqrs$increment,    $minvalue$maxvalue,
                                
$restartvalue$cachevalue$cycledvalue$startvalue) {

        
$sql '';
        
/* vars are cleaned in _alterSequence */
        
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(
$restartvalue) && ($restartvalue != $seqrs->fields['last_value'])) $sql .= " RESTART {$restartvalue}";
        if (!empty(
$cachevalue) && ($cachevalue != $seqrs->fields['cache_value'])) $sql .= " CACHE {$cachevalue}";
        if (!empty(
$startvalue) && ($startvalue != $seqrs->fields['start_value'])) $sql .= " START {$startvalue}";
        
// toggle cycle yes/no
        
if (!is_null($cycledvalue))    $sql .= (!$cycledvalue ' NO ' '') . " CYCLE";
        if (
$sql != '') {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$sql "ALTER SEQUENCE \"{$f_schema}\".\"{$seqrs->fields['seqname']}\" {$sql}";
            return 
$this->execute($sql);
        }
        return 
0;
    }

    
/**
     * 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 $restartvalue The starting value
     * @param $cachevalue The cache value
     * @param $cycledvalue True if cycled, false otherwise
     * @param $startvalue The sequence start value when issueing a restart
     * @return 0 success
     * @return -3 rename error
     * @return -4 comment error
     * @return -5 owner error
     * @return -6 get sequence props error
     * @return -7 schema error
     */
    
protected
    function 
_alterSequence($seqrs$name$comment$owner$schema$increment,
    
$minvalue$maxvalue$restartvalue$cachevalue$cycledvalue$startvalue) {

        
$this->fieldArrayClean($seqrs->fields);

        
// Comment
        
$status $this->setComment('SEQUENCE'$seqrs->fields['seqname'], ''$comment);
        if (
$status != 0)
            return -
4;

        
// 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($restartvalue);
        
$this->clean($cachevalue);
        
$this->clean($cycledvalue);
        
$this->clean($startvalue);
        
$status $this->alterSequenceProps($seqrs$increment,    $minvalue,
            
$maxvalue$restartvalue$cachevalue$cycledvalue$startvalue);
        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;
    }

    
/**
     * Alters a sequence
     * @param $sequence The name of the sequence
     * @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 $restartvalue The starting value
     * @param $cachevalue The cache value
     * @param $cycledvalue True if cycled, false otherwise
     * @param $startvalue The sequence start value when issueing a restart
     * @return 0 success
     * @return -1 transaction error
     * @return -2 get existing sequence error
     * @return $this->_alterSequence error code
     */
    
function alterSequence($sequence$name$comment$owner=null$schema=null$increment=null,
    
$minvalue=null$maxvalue=null$restartvalue=null$cachevalue=null$cycledvalue=null$startvalue=null) {

        
$this->fieldClean($sequence);

        
$data $this->getSequence($sequence);

        if (
$data->recordCount() != 1)
            return -
2;

        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
$status $this->_alterSequence($data$name$comment$owner$schema$increment,
                
$minvalue$maxvalue$restartvalue$cachevalue$cycledvalue$startvalue);

        if (
$status != 0) {
            
$this->rollbackTransaction();
            return 
$status;
        }

        return 
$this->endTransaction();
    }

    
/**
     * Drops a given sequence
     * @param $sequence Sequence name
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropSequence($sequence$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($sequence);

        
$sql "DROP SEQUENCE \"{$f_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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='{$c_schema}'";

        return 
$this->selectSet($sql);
    }

    
/**
     * Returns a list of all views in the database
     * @return All views
     */
    
function getViews() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_schema}') AND (c.relkind = 'v'::\"char\")
            ORDER BY relname"
;

        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$definitiontrue$comment);
    }

    
/**
     * 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 -1;

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($viewname);

        
// Note: $definition not cleaned

        
$sql "CREATE ";
        if (
$replace$sql .= "OR REPLACE ";
        
$sql .= "VIEW \"{$f_schema}\".\"{$viewname}\" AS {$definition}";

        
$status $this->execute($sql);
        if (
$status) {
            
$this->rollbackTransaction();
            return -
1;
        }

        if (
$comment != '') {
            
$status $this->setComment('VIEW'$viewname''$comment);
            if (
$status) {
                
$this->rollbackTransaction();
            return -
1;
            }
        }

        return 
$this->endTransaction();
    }

    
/**
     * Rename a view
     * @param $vwrs The view recordSet returned by getView()
     * @param $name The new view's name
     * @return 0 success
     */
    
function alterViewName($vwrs$name) {
        
// Rename (only if name has changed)
        /* $vwrs and $name are cleaned in _alterView */
        
if (!empty($name) && ($name != $vwrs->fields['relname'])) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$sql "ALTER VIEW \"{$f_schema}\".\"{$vwrs->fields['relname']}\" RENAME TO \"{$name}\"";
            
$status =  $this->execute($sql);
            if (
$status == 0)
                
$vwrs->fields['relname'] = $name;
            else
                return 
$status;
        }
        return 
0;
    }

    
/**
     * Alter a view's owner
     * @param $vwrs The view recordSet returned by getView()
     * @param $name The new view's owner
     * @return 0 success
     */
    
function alterViewOwner($vwrs$owner null) {
        
/* $vwrs and $owner are cleaned in _alterView */
        
if ((!empty($owner)) && ($vwrs->fields['relowner'] != $owner)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
// 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 \"{$f_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) {
        
/* $vwrs and $schema are cleaned in _alterView */
        
if (!empty($schema) && ($vwrs->fields['nspname'] != $schema)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
// If tablespace has been changed, then do the alteration.  We
            // don't want to do this unnecessarily.
            
$sql "ALTER TABLE \"{$f_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
        
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;

        
// Schema
        
$this->fieldClean($schema);
        
$status $this->alterViewSchema($vwrs$schema);
        if (
$status != 0) return -6;

        return 
0;
    }

    
/**
     * 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 transaction error
     * @return -2 get existing view error
     * @return $this->_alterView error code
     */
    
function alterView($view$name$owner$schema$comment) {

        
$data $this->getView($view);
        if (
$data->recordCount() != 1)
            return -
2;

        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
$status $this->_alterView($data$name$owner$schema$comment);

        if (
$status != 0) {
            
$this->rollbackTransaction();
            return 
$status;
        }

        return 
$this->endTransaction();
    }

    
/**
     * 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 dropView($viewname$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($viewname);

        
$sql "DROP VIEW \"{$f_schema}\".\"{$viewname}\"";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($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);
    }

    
/** 
     * test if a table has been clustered on an index
     * @param $table The table to test
     * 
     * @return true if the table has been already clustered
     */
    
function alreadyClustered($table) {
        
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "SELECT i.indisclustered
            FROM pg_catalog.pg_class c, pg_catalog.pg_index i
            WHERE c.relname = '
{$table}'
                AND c.oid = i.indrelid AND i.indisclustered
                AND c.relnamespace = (SELECT oid FROM pg_catalog.pg_namespace
                    WHERE nspname='
{$c_schema}')
                "
;
        
        
$v $this->selectSet($sql);
        
        if (
$v->recordCount() == 0)
            return 
false;
            
        return 
true;
    }
    
    
/**
     * Creates an index
     * @param $name The index name
     * @param $table The table on which to add the index
     * @param $columns An array of columns that form the index
     *                 or a string expression for a functional index
     * @param $type The index type
     * @param $unique True if unique, false otherwise
     * @param $where Index predicate ('' for none)
     * @param $tablespace The tablespaces ('' means none/default)
     * @return 0 success
     */
    
function createIndex($name$table$columns$type$unique$where$tablespace$concurrently) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);
        
$this->fieldClean($table);

        
$sql "CREATE";
        if (
$unique$sql .= " UNIQUE";
        
$sql .= " INDEX";
        if (
$concurrently$sql .= " CONCURRENTLY";
        
$sql .= " \"{$name}\" ON \"{$f_schema}\".\"{$table}\" USING {$type} ";

        if (
is_array($columns)) {
            
$this->arrayClean($columns);
            
$sql .= "(\"" implode('","'$columns) . "\")";
        } else {
            
$sql .= "(" $columns .")";
        }

        
// Tablespace
        
if ($this->hasTablespaces() && $tablespace != '') {
            
$this->fieldClean($tablespace);
            
$sql .= " TABLESPACE \"{$tablespace}\"";
        }

        
// Predicate
        
if (trim($where) != '') {
            
$sql .= " WHERE ({$where})";
        }

        return 
$this->execute($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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($index);

        
$sql "DROP INDEX \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);
        switch(
$type) {
            case 
'DATABASE':
                
$sql "REINDEX {$type} \"{$name}\"";
                if (
$force$sql .= ' FORCE';
                break;
            case 
'TABLE':
            case 
'INDEX':
                
$sql "REINDEX {$type} \"{$f_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($table=''$index='') {
        
        
$sql 'CLUSTER';
        
        
// 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
        
        
if (!empty($table)) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($table);
            
$sql .= " \"{$f_schema}\".\"{$table}\"";
            
            if (!empty(
$index)) {
                
$this->fieldClean($index);
                
$sql .= " USING \"{$index}\"";
            }
        }

        return 
$this->execute($sql);
    }

    
// 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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) {

        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->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  \$patern\$^\\[.*:(.*)\\]$\$patern\$)) 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='{$c_schema}'";

        
$rs $this->selectSet($sql);

        if (
$rs->EOF$max_col 0;
        else 
$max_col $rs->fields['nb'];

        
$sql '
            SELECT
                c.oid AS conid, 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, f1.attnum AS p_attnum, f2.attname as f_field,
                f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment,
                c.conrelid, c.confrelid
            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$c_schema);

        return 
$this->selectSet($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;
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldArrayClean($fields);
        
$this->fieldClean($name);
        
$this->fieldClean($tablespace);

        
$sql "ALTER TABLE \"{$f_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);
    }

    
/**
     * 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;
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldArrayClean($fields);
        
$this->fieldClean($name);
        
$this->fieldClean($tablespace);

        
$sql "ALTER TABLE \"{$f_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 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 '') {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($name);
        
// @@ How the heck do you clean a definition???

        
$sql "ALTER TABLE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$c_table $table;
        
$this->fieldClean($table);
        
$this->clean($c_table);
        
$this->clean($name);

        
// Begin transaction
        
$status $this->beginTransaction();
        if (
$status != 0) return -2;

        
// Properly lock the table
        
$sql "LOCK TABLE \"{$f_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='{$c_table}'
            AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
            nspname = '
{$c_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='
{$c_table}'
                        AND relnamespace = (SELECT oid FROM pg_catalog.pg_namespace WHERE
                        nspname = '
{$c_schema}')))
                    WHERE relname='
{$c_table}'";
           
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }

        
// Otherwise, close the transaction
        
return $this->endTransaction();
    }

    
/**
     * 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 addForeignKey($table$targschema$targtable$sfields$tfields$upd_action$del_action,
    
$match$deferrable$initially$name '') {
        if (!
is_array($sfields) || sizeof($sfields) == ||
            !
is_array($tfields) || sizeof($tfields) == 0) return -1;
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($targschema);
        
$this->fieldClean($targtable);
        
$this->fieldArrayClean($sfields);
        
$this->fieldArrayClean($tfields);
        
$this->fieldClean($name);

        
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" ADD ";
        if (
$name != ''$sql .= "CONSTRAINT \"{$name}\" ";
        
$sql .= "FOREIGN KEY (\"" join('","'$sfields) . "\") ";
        
// Target table needs to be fully qualified
        
$sql .= "REFERENCES \"{$targschema}\".\"{$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);
    }

    
/**
     * 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
     */
    
function dropConstraint($constraint$relation$type$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($constraint);
        
$this->fieldClean($relation);

        
$sql "ALTER TABLE \"{$f_schema}\".\"{$relation}\" DROP CONSTRAINT \"{$constraint}\"";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($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;
        
        
$this->clean($tables[0]['tablename']);
        
$this->clean($tables[0]['schemaname']);
        
$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++) {
            
$this->clean($tables[$i]['tablename']);
            
$this->clean($tables[$i]['schemaname']);
            
$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], 0strlen($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);
    }

    
/**
     * 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);

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

        
$c_schema $this->_schema;
        
$this->clean($c_schema);

        
$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='
{$c_schema}'))
            ORDER BY 1,2,3
        "
;

        return 
$this->selectSet($sql);
        }

    
// Domain functions

    /**
     * Gets all information for a single domain
     * @param $domain The name of the domain to fetch
     * @return A recordset
     */
    
function getDomain($domain) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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 = '
{$c_schema}')";

        return 
$this->selectSet($sql);
        }

    
/**
     * Return all domains in current schema.  Excludes domain constraints.
     * @return All tables, sorted alphabetically
     */
    
function getDomains() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
        
$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='
{$c_schema}')
            ORDER BY t.typname"
;

        return 
$this->selectSet($sql);
    }

    
/**
     * Get domain constraints
     * @param $domain The name of the domain whose constraints to fetch
     * @return A recordset
     */
    
function getDomainConstraints($domain) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($domain);

        
$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 = '
{$c_schema}')
                )
            ORDER BY conname"
;

        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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($domain);

        
$sql "CREATE DOMAIN \"{$f_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($type9);
                    
$sql .= "timestamp({$length}){$qual}";
                    break;
                case 
'time with time zone':
                case 
'time without time zone':
                    
$qual substr($type4);
                    
$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);
    }

    
/**
     * 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 alterDomain($domain$domdefault$domnotnull$domowner) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($domain);
        
$this->fieldClean($domowner);

        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
// Default
        
if ($domdefault == '')
            
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP DEFAULT";
        else
            
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET DEFAULT {$domdefault}";

        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
2;
        }

        
// NOT NULL
        
if ($domnotnull)
            
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" SET NOT NULL";
        else
            
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP NOT NULL";

        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
3;
        }

        
// Owner
        
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" OWNER TO \"{$domowner}\"";

        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }

        return 
$this->endTransaction();
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($domain);

        
$sql "DROP DOMAIN \"{$f_schema}\".\"{$domain}\"";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($sql);
    }

    
/**
     * 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 '') {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($domain);
        
$this->fieldClean($name);

        
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" ADD ";
        if (
$name != ''$sql .= "CONSTRAINT \"{$name}\" ";
        
$sql .= "CHECK ({$definition})";

        return 
$this->execute($sql);
    }

    
/**
     * 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 dropDomainConstraint($domain$constraint$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($domain);
        
$this->fieldClean($constraint);

        
$sql "ALTER DOMAIN \"{$f_schema}\".\"{$domain}\" DROP CONSTRAINT \"{$constraint}\"";
        if (
$cascade$sql .= " CASCADE";

        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, 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 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 {
            
$c_schema $this->_schema;
            
$this->clean($c_schema);
            
$where "n.nspname = '{$c_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 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;
    }

    
/**
     * 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$commenttrue);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return 
$status;
        }

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);

        
// Rename the function, if necessary
        
$this->fieldClean($newname);
        
/* $funcname is escaped in createFunction */
        
if ($funcname != $newname) {
            
$sql "ALTER FUNCTION \"{$f_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 \"{$f_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);
            
/* $funcschema is escaped in createFunction */
            
if ($funcschema != $newschema) { 
                
$sql "ALTER FUNCTION \"{$f_schema}\".\"{$funcname}\"({$args}) SET SCHEMA \"{$newschema}\"";
                
$status $this->execute($sql);
                if (
$status != 0) {
                    
$this->rollbackTransaction();
                    return -
7;
                }
            }
        }

        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 $comment Comment for the function
     * @param $replace (optional) True if OR REPLACE, false for normal
     * @return 0 success
     * @return -3 create function failed
     * @return -4 set comment failed
     */
    
function createFunction($funcname$args$returns$definition$language$flags$setof$cost$rows$comment$replace false) {
        
        
// Begin a transaction
        
$status $this->beginTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }
        
        
$this->fieldClean($funcname);
        
$this->clean($args);
        
$this->fieldClean($language);
        
$this->arrayClean($flags);
        
$this->clean($cost);
        
$this->clean($rows);
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);

        
$sql "CREATE";
        if (
$replace$sql .= " OR REPLACE";
        
$sql .= " FUNCTION \"{$f_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 <> ){
            
$sql .= " ROWS {$rows}";
        }

        
// Add flags
        
foreach ($flags as  $v) {
            
// Skip default flags
            
if ($v == '') continue;
            else 
$sql .= "\n{$v}";
        }

        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
3;
        }

        
/* set the comment */
        
$status $this->setComment('FUNCTION'"\"{$funcname}\"({$args})"null$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }

        return 
$this->endTransaction();
    }

    
/**
     * 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 dropFunction($function_oid$cascade) {
        
// Function comes in with $object as function OID
        
$fn $this->getFunction($function_oid);
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($fn->fields['proname']);

        
$sql "DROP FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($sql);
    }

    
// 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 types, 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 {
            
$c_schema $this->_schema;
            
$this->clean($c_schema);
            
$where "n.nspname = '{$c_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);
    }

    
/**
     * Creates a new type
     * @param ...
     * @return 0 success
     */
    
function createType($typname$typin$typout$typlen$typdef,
                        
$typelem$typdelim$typbyval$typalign$typstorage) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($typname);
        
$this->fieldClean($typin);
        
$this->fieldClean($typout);

        
$sql "
            CREATE TYPE \"
{$f_schema}\".\"{$typname}\" (
                INPUT = \"
{$typin}\",
                OUTPUT = \"
{$typout}\",
                INTERNALLENGTH = 
{$typlen}";
        if (
$typdef != ''$sql .= ", DEFAULT = {$typdef}";
        if (
$typelem != ''$sql .= ", ELEMENT = {$typelem}";
        if (
$typdelim != ''$sql .= ", DELIMITER = {$typdelim}";
        if (
$typbyval$sql .= ", PASSEDBYVALUE, ";
        if (
$typalign != ''$sql .= ", ALIGNMENT = {$typalign}";
        if (
$typstorage != ''$sql .= ", STORAGE = {$typstorage}";

        
$sql .= ")";

        return 
$this->execute($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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($typname);

        
$sql "DROP TYPE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);

        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 \"{$f_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''$typcommenttrue);
            if (
$status) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        return 
$this->endTransaction();

    }

    
/**
     * Get defined values for a given enum
     * @return A recordset
     */
    
function getEnumValues($name) {
        
$this->clean($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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);

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

        
$found false;
        
$first true;
        
$comment_sql ''// Accumulate comments for the columns
        
$sql "CREATE TYPE \"{$f_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 \"{$f_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''$typcommenttrue);
            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() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "
            SELECT *
            FROM pg_catalog.pg_rules
            WHERE
                schemaname='
{$c_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$actiontrue);
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);
        
$this->fieldClean($table);
        if (!
in_array($event$this->rule_events)) return -1;

        
$sql "CREATE";
        if (
$replace$sql .= " OR REPLACE";
        
$sql .= " RULE \"{$name}\" AS ON {$event} TO \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($rule);
        
$this->fieldClean($relation);

        
$sql "DROP RULE \"{$rule}\" ON \"{$f_schema}\".\"{$relation}\"";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($sql);
    }

    
// 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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 '') {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_schema}'))
                AND ( tgconstraint = 0 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
     * @return The trigger definition string
     */
    
function getTriggerDef($trigger) {

        
$this->fieldArrayClean($trigger);
        
// Constants to figure out tgtype
        
if (!defined('TRIGGER_TYPE_ROW')) define ('TRIGGER_TYPE_ROW', (<< 0));
        if (!
defined('TRIGGER_TYPE_BEFORE')) define ('TRIGGER_TYPE_BEFORE', (<< 1));
        if (!
defined('TRIGGER_TYPE_INSERT')) define ('TRIGGER_TYPE_INSERT', (<< 2));
        if (!
defined('TRIGGER_TYPE_DELETE')) define ('TRIGGER_TYPE_DELETE', (<< 3));
        if (!
defined('TRIGGER_TYPE_UPDATE')) define ('TRIGGER_TYPE_UPDATE', (<< 4));

        
$trigger['tgisconstraint'] = $this->phpBool($trigger['tgisconstraint']);
        
$trigger['tgdeferrable'] = $this->phpBool($trigger['tgdeferrable']);
        
$trigger['tginitdeferred'] = $this->phpBool($trigger['tginitdeferred']);

        
// Constraint trigger or normal trigger
        
if ($trigger['tgisconstraint'])
            
$tgdef 'CREATE CONSTRAINT TRIGGER ';
        else
            
$tgdef 'CREATE TRIGGER ';

        
$tgdef .= "\"{$trigger['tgname']}\" ";

        
// Trigger type
        
$findx 0;
        if ((
$trigger['tgtype'] & TRIGGER_TYPE_BEFORE) == TRIGGER_TYPE_BEFORE)
            
$tgdef .= 'BEFORE';
        else
            
$tgdef .= 'AFTER';

        if ((
$trigger['tgtype'] & TRIGGER_TYPE_INSERT) == TRIGGER_TYPE_INSERT) {
            
$tgdef .= ' INSERT';
            
$findx++;
        }
        if ((
$trigger['tgtype'] & TRIGGER_TYPE_DELETE) == TRIGGER_TYPE_DELETE) {
            if (
$findx 0)
                
$tgdef .= ' OR DELETE';
            else {
                
$tgdef .= ' DELETE';
                
$findx++;
            }
        }
        if ((
$trigger['tgtype'] & TRIGGER_TYPE_UPDATE) == TRIGGER_TYPE_UPDATE) {
            if (
$findx 0)
                
$tgdef .= ' OR UPDATE';
            else
                
$tgdef .= ' UPDATE';
        }

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
// Table name
        
$tgdef .= " ON \"{$f_schema}\".\"{$trigger['relname']}\" ";

        
// Deferrability
        
if ($trigger['tgisconstraint']) {
            if (
$trigger['tgconstrrelid'] != 0) {
                
// Assume constrelname is not null
                
$tgdef .= " FROM \"{$trigger['tgconstrrelname']}\" ";
            }
            if (!
$trigger['tgdeferrable'])
                
$tgdef .= 'NOT ';
            
$tgdef .= 'DEFERRABLE INITIALLY ';
            if (
$trigger['tginitdeferred'])
                
$tgdef .= 'DEFERRED ';
            else
                
$tgdef .= 'IMMEDIATE ';
        }

        
// Row or statement
        
if ($trigger['tgtype'] & TRIGGER_TYPE_ROW == TRIGGER_TYPE_ROW)
            
$tgdef .= 'FOR EACH ROW ';
        else
            
$tgdef .= 'FOR EACH STATEMENT ';

        
// Execute procedure
        
$tgdef .= "EXECUTE PROCEDURE \"{$trigger['tgfname']}\"(";

        
// Parameters
        // Escape null characters
        
$v addCSlashes($trigger['tgargs'], "\0");
        
// Split on escaped null characters
        
$params explode('\\000'$v);
        for (
$findx 0$findx $trigger['tgnargs']; $findx++) {
            
$param "'" str_replace('\'''\\\''$params[$findx]) . "'";
            
$tgdef .= $param;
            if (
$findx < ($trigger['tgnargs'] - 1))
                
$tgdef .= ', ';
        }

        
// Finish it off
        
$tgdef .= ')';

        return 
$tgdef;
    }

    
/**
     * Returns a list of all functions that can be used in triggers
     */
    
function getTriggerFunctions() {
        return 
$this->getFunctions(true'trigger');
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);
        
$this->fieldClean($tgproc);

        
/* No Statement Level Triggers in PostgreSQL (by now) */
        
$sql "CREATE TRIGGER \"{$tgname}\" {$tgtime}
                
{$tgevent} ON \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
$this->fieldClean($trigger);
        
$this->fieldClean($name);

        
$sql "ALTER TRIGGER \"{$trigger}\" ON \"{$f_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
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropTrigger($tgname$table$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);

        
$sql "DROP TRIGGER \"{$tgname}\" ON \"{$f_schema}\".\"{$table}\"";
        if (
$cascade$sql .= " CASCADE";

        return 
$this->execute($sql);
    }

    
/**
     * 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);

        
$sql "ALTER TABLE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);

        
$sql "ALTER TABLE \"{$f_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() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
// 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='
{$c_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,
                oprcanmerge,
                oprcom::pg_catalog.regoperator AS oprcom,
                oprnegate::pg_catalog.regoperator AS oprnegate,
                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);
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($opr->fields['oprname']);

        
$sql "DROP OPERATOR \"{$f_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() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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 '') {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
         
$this->fieldClean($cfgname);

         
$sql "CREATE TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$cfgname}\" (";
         if (
$parser != '') {
            
$this->fieldClean($parser['schema']);
            
$this->fieldClean($parser['parser']);
            
$parser "\"{$parser['schema']}\".\"{$parser['parser']}\"";
            
$sql .= " PARSER = {$parser}";
        }
         if (
$template != '') {
            
$this->fieldClean($template['schema']);
            
$this->fieldClean($template['name']);
             
$sql .= " COPY = \"{$template['schema']}\".\"{$template['name']}\"";
         }
        
$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 != '') {
             
$status $this->setComment('TEXT SEARCH CONFIGURATION'$cfgname''$comment);
             if (
$status != 0) {
                 
$this->rollbackTransaction();
                 return -
1;
             }

             return 
$this->endTransaction();
         }

         return 
0;
     }

     
/**
      * Returns available FTS configurations
     * @param $all if false, returns schema qualified FTS confs
     * 
     * @return A recordset
      */
     
function getFtsConfigurations($all true) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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)"
;

        if (!
$all)
            
$sql.= " AND  n.nspname='{$c_schema}'\n";
        
        
$sql.= "ORDER BY 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) {
         
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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}'
                AND n.nspname='
{$c_schema}'";

         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
     * 
     * @return RecordSet
      */
     
function getFtsConfigurationMap($ftscfg) {

         
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->fieldClean($ftscfg);

         
$oidSet $this->selectSet("SELECT c.oid
            FROM pg_catalog.pg_ts_config AS c
                LEFT JOIN pg_catalog.pg_namespace n ON (n.oid = c.cfgnamespace)
            WHERE c.cfgname = '
{$ftscfg}'
                AND n.nspname='
{$c_schema}'");

         
$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 FTS parsers available
     * @param $all if false, return only Parsers from the current schema
     *
     * @return RecordSet
      */
     
function getFtsParsers($all true) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
         
$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)"
;

        if (!
$all)
            
$sql.= " AND n.nspname='{$c_schema}'\n";

        
$sql.= "ORDER BY name";

         return 
$this->selectSet($sql);
     }

     
/**
      * Returns FTS dictionaries available
     * @param $all if false, return only Dics from the current schema
     *
     * @returns RecordSet
      */
     
function getFtsDictionaries($all true) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
         
$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)"
;

        if (!
$all)
            
$sql.= " AND n.nspname='{$c_schema}'\n";

        
$sql.= "ORDER BY 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 name;"
;

         return 
$this->selectSet($sql);
     }

     
/**
      * Drops FTS coniguration
     * @param $ftscfg The configuration's name
     * @param $cascade Cascade to dependenced objects
     *
     * @return 0 on success
      */
     
function dropFtsConfiguration($ftscfg$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
         
$this->fieldClean($ftscfg);

         
$sql "DROP TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\"";
         if (
$cascade$sql .=  ' CASCADE';

         return 
$this->execute($sql);
     }

     
/**
      * Drops FTS dictionary
     * @param $ftsdict The dico's name
     * @param $cascade Cascade to dependenced objects
      *
      * @todo Support of dictionary templates dropping
     * @return 0 on success
      */
     
function dropFtsDictionary($ftsdict$cascade) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
         
$this->fieldClean($ftsdict);

         
$sql "DROP TEXT SEARCH DICTIONARY";
         
$sql .= " \"{$f_schema}\".\"{$ftsdict}\"";
         if (
$cascade$sql .= ' CASCADE';

         return 
$this->execute($sql);
     }

     
/**
      * Alters FTS configuration
     * @param $cfgname The conf's name
     * @param $comment A comment on for the conf
     * @param $name The new conf name
     *
     * @return 0 on success
      */
     
function updateFtsConfiguration($cfgname$comment$name) {

         
$status $this->beginTransaction();
         if (
$status != 0) {
             
$this->rollbackTransaction();
             return -
1;
         }

        
$this->fieldClean($cfgname);

         
$status $this->setComment('TEXT SEARCH CONFIGURATION'$cfgname''$comment);
         if (
$status != 0) {
             
$this->rollbackTransaction();
             return -
1;
         }

         
// Only if the name has changed
         
if ($name != $cfgname) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($name);

             
$sql "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$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 '') {

        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
         
$this->fieldClean($dictname);
         
$this->fieldClean($template);
         
$this->fieldClean($lexize);
         
$this->fieldClean($init);
         
$this->fieldClean($option);

         
$sql "CREATE TEXT SEARCH";
         if (
$isTemplate) {
             
$sql .= " TEMPLATE \"{$f_schema}\".\"{$dictname}\" (";
             if (
$lexize != ''$sql .= " LEXIZE = {$lexize}";
             if (
$init != ''$sql .= ", INIT = {$init}";
            
$sql .= ")";
             
$whatToComment 'TEXT SEARCH TEMPLATE';
         } else {
             
$sql .= " DICTIONARY \"{$f_schema}\".\"{$dictname}\" (";
             if (
$template != '') {        
                
$this->fieldClean($template['schema']);
                
$this->fieldClean($template['name']);
                
$template "\"{$template['schema']}\".\"{$template['name']}\"";
            
                
$sql .= " TEMPLATE = {$template}";
            }
             if (
$option != ''$sql .= ", {$option}";
            
$sql .= ")";
             
$whatToComment 'TEXT SEARCH DICTIONARY';
         }

        
/* if comment, begin a transaction to
         * run both commands */
         
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
     * @param $dictname The dico's name 
     * @param $comment The comment
     * @param $name The new dico's name
     *
     * @return 0 on success
      */
     
function updateFtsDictionary($dictname$comment$name) {

         
$status $this->beginTransaction();
         if (
$status != 0) {
             
$this->rollbackTransaction();
             return -
1;
         }
        
        
$this->fieldClean($dictname);
         
$status $this->setComment('TEXT SEARCH DICTIONARY'$dictname''$comment);
         if (
$status != 0) {
             
$this->rollbackTransaction();
             return -
1;
         }

         
// Only if the name has changed
         
if ($name != $dictname) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
             
$this->fieldClean($name);

             
$sql "ALTER TEXT SEARCH DICTIONARY \"{$f_schema}\".\"{$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 RecordSet of FTS dictionary information
      */
     
function getFtsDictionaryByName($ftsdict) {
    
         
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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)
               AND n.nspname='
{$c_schema}'
            ORDER BY 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) {

         if (
count($mapping) > 0) {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($ftscfg);
            
$this->fieldClean($dictname);
            
$this->arrayClean($mapping);
        
             switch (
$action) {
                 case 
'alter':
                     
$whatToDo "ALTER";
                     break;
                 case 
'drop':
                     
$whatToDo "DROP";
                     break;
                 default:
                     
$whatToDo "ADD";
                     break;
             }

             
$sql "ALTER TEXT SEARCH CONFIGURATION \"{$f_schema}\".\"{$ftscfg}\" {$whatToDo} MAPPING FOR ";
             
$sql .= implode(","$mapping);
             if (
$action != 'drop' && !empty($dictname)) {
                 
$sql .= " WITH {$dictname}";
             }

             return 
$this->execute($sql);
         }
        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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
         
$this->clean($ftscfg);
         
$this->clean($mapping);

         
$oidSet $this->selectSet("SELECT c.oid, cfgparser
            FROM pg_catalog.pg_ts_config AS c
                LEFT JOIN pg_catalog.pg_namespace AS n ON n.oid = c.cfgnamespace
            WHERE c.cfgname = '
{$ftscfg}'
                AND n.nspname='
{$c_schema}'");
                
         
$oid $oidSet->fields['oid'];
         
$cfgparser $oidSet->fields['cfgparser'];

         
$tokenIdSet $this->selectSet("SELECT tokid
            FROM pg_catalog.ts_token_type(
{$cfgparser})
            WHERE alias = '
{$mapping}'");

         
$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)
     * @param $ftscfg The config's name that use the parser
     *
     * @return 0 on success
      */
     
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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($name);
        
$this->fieldClean($basetype);
        
$this->fieldClean($sfunc);
        
$this->fieldClean($stype);
        
$this->fieldClean($ffunc);
        
$this->fieldClean($initcond);
        
$this->fieldClean($sortop);

        
$this->beginTransaction();

        
$sql "CREATE AGGREGATE \"{$f_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) {
        
/* this function is called from alterAggregate where params are cleaned */
        
$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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($aggrname);
        
$this->fieldClean($aggrtype);

        
$sql "DROP AGGREGATE \"{$f_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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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() {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$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='
{$c_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($aggrname);
        
$this->fieldClean($newaggrowner);
        
$sql "ALTER AGGREGATE \"{$f_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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($aggrname);
        
$this->fieldClean($newaggrschema);
        
$sql "ALTER AGGREGATE \"{$f_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->fieldClean($newaggrname);
        
$this->fieldClean($newaggrowner);
        
$this->fieldClean($newaggrschema);

        
$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, useconfig
            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, useconfig
            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$rolename0'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$o0'CASCADE');
                    if (
$status != 0) return -1;
                }
            }
        }

        
//adminmembers
        
$old explode(','$adminmembersold);
        foreach (
$adminmembers as $m) {
            if (!
in_array($m$old)) {
                
$status $this->grantRole($rolename$m1);
                if (
$status != 0) return -1;
            }
        }
        if(
$adminmembersold)
        {
            foreach (
$old as $o) {
                if (!
in_array($o$adminmembers)) {
                    
$status $this->revokeRole($rolename$o1'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;

        if (
$rolename != $newrolename){
            
$status $this->renameRole($rolename$newrolename);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
3;
            }
            
$rolename $newrolename;
        }

        
$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 
$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;

        if (
$username != $newname){
            
$status $this->renameUser($username$newname);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
3;
            }
            
$username $newname;
        }

        
$status $this->setUser($username$password$createdb$createuser$expiry);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
2;
        }

        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
     * @param $acl The ACL to parse (of type aclitem[])
     * @return Privileges array
     */
    
function _parseACL($acl) {
        
// Take off the first and last characters (the braces)
        
$acl substr($acl1strlen($acl) - 2);

        
// Pick out individual ACE's by carefully parsing.  This is necessary in order
        // to cope with usernames and stuff that contain commas
        
$aces = array();
        
$i $j 0;
        
$in_quotes false;
        while (
$i strlen($acl)) {
            
// If current char is a double quote and it's not escaped, then
            // enter quoted bit
            
$char substr($acl$i1);
            if (
$char == '"' && ($i == || substr($acl$i 11) != '\\'))
                
$in_quotes = !$in_quotes;
            elseif (
$char == ',' && !$in_quotes) {
                
// Add text so far to the array
                
$aces[] = substr($acl$j$i $j);
                
$j $i 1;
            }
            
$i++;
        }
        
// Add final text to the array
        
$aces[] = substr($acl$j);

        
// Create the array to be returned
        
$temp = array();

        
// For each ACE, generate an entry in $temp
        
foreach ($aces as $v) {

            
// If the ACE begins with a double quote, strip them off both ends
            // and unescape backslashes and double quotes
            
$unquote false;
            if (
strpos($v'"') === 0) {
                
$v substr($v1strlen($v) - 2);
                
$v str_replace('\\"''"'$v);
                
$v str_replace('\\\\''\\'$v);
            }

            
// Figure out type of ACE (public, user or group)
            
if (strpos($v'=') === 0)
                
$atype 'public';
            else if (
$this->hasRoles()) {
                
$atype 'role';
            }
            else if (
strpos($v'group ') === 0) {
                
$atype 'group';
                
// Tear off 'group' prefix
                
$v substr($v6);
            }
            else
                
$atype 'user';

            
// Break on unquoted equals sign...
            
$i 0;
            
$in_quotes false;
            
$entity null;
            
$chars null;
            while (
$i strlen($v)) {
                
// If current char is a double quote and it's not escaped, then
                // enter quoted bit
                
$char substr($v$i1);
                
$next_char substr($v$i 11);
                if (
$char == '"' && ($i == || $next_char != '"')) {
                    
$in_quotes = !$in_quotes;
                }
                
// Skip over escaped double quotes
                
elseif ($char == '"' && $next_char == '"') {
                    
$i++;
                }
                elseif (
$char == '=' && !$in_quotes) {
                    
// Split on current equals sign
                    
$entity substr($v0$i);
                    
$chars substr($v$i 1);
                    break;
                }
                
$i++;
            }

            
// Check for quoting on entity name, and unescape if necessary
            
if (strpos($entity'"') === 0) {
                
$entity substr($entity1strlen($entity) - 2);
                
$entity str_replace('""''"'$entity);
            }

            
// New row to be added to $temp
            // (type, grantee, privileges, grantor, grant option?
            
$row = array($atype$entity, array(), '', array());

            
// Loop over chars and add privs to $row
            
for ($i 0$i strlen($chars); $i++) {
                
// Append to row's privs list the string representing
                // the privilege
                
$char substr($chars$i1);
                if (
$char == '*')
                    
$row[4][] = $this->privmap[substr($chars$i 11)];
                elseif (
$char == '/') {
                    
$grantor substr($chars$i 1);
                    
// Check for quoting
                    
if (strpos($grantor'"') === 0) {
                        
$grantor substr($grantor1strlen($grantor) - 2);
                        
$grantor str_replace('""''"'$grantor);
                    }
                    
$row[3] = $grantor;
                    break;
                }
                else {
                    if (!isset(
$this->privmap[$char]))
                        return -
3;
                    else
                        
$row[2][] = $this->privmap[$char];
                }
            }

            
// Append row to temp
            
$temp[] = $row;
        }

        return 
$temp;
    }

    
/**
     * 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)
     * @param $table Optional, column's table if type = column
     * @return Privileges array
     * @return -1 invalid type
     * @return -2 object not found
     * @return -3 unknown privilege type
     */
    
function getPrivileges($object$type$table null) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($object);

        switch (
$type) {
            case 
'column':
                
$this->clean($table);
                
$sql "
                    SELECT E'{' || pg_catalog.array_to_string(attacl, E',') || E'}' as acl
                    FROM pg_catalog.pg_attribute a
                        LEFT JOIN pg_catalog.pg_class c ON (a.attrelid = c.oid)
                        LEFT JOIN pg_catalog.pg_namespace n ON (c.relnamespace=n.oid)
                    WHERE n.nspname='
{$c_schema}'
                        AND c.relname='
{$table}'
                        AND a.attname='
{$object}'";
                break;
            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='
{$c_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);
    }

    
/**
     * Grants a privilege to a user, group or public
     * @param $mode 'GRANT' or 'REVOKE';
     * @param $type The type of object
     * @param $object The name of the object
     * @param $public True to grant to public, false otherwise
     * @param $usernames The array of usernames to grant privs to.
     * @param $groupnames The array of group names to grant privs to.
     * @param $privileges The array of privileges to grant (eg. ('SELECT', 'ALL PRIVILEGES', etc.) )
     * @param $grantoption True if has grant option, false otherwise
     * @param $cascade True for cascade revoke, false otherwise
     * @param $table the column's table if type=column
     * @return 0 success
     * @return -1 invalid type
     * @return -2 invalid entity
     * @return -3 invalid privileges
     * @return -4 not granting to anything
     * @return -4 invalid mode
     */
    
function setPrivileges($mode$type$object$public$usernames$groupnames,
        
$privileges$grantoption$cascade$table
    
) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldArrayClean($usernames);
        
$this->fieldArrayClean($groupnames);

        
// Input checking
        
if (!is_array($privileges) || sizeof($privileges) == 0) return -3;
        if (!
is_array($usernames) || !is_array($groupnames) ||
            (!
$public && sizeof($usernames) == && sizeof($groupnames) == 0)) return -4;
        if (
$mode != 'GRANT' && $mode != 'REVOKE') return -5;

        
$sql $mode;

        
// Grant option
        
if ($this->hasGrantOption() && $mode == 'REVOKE' && $grantoption) {
            
$sql .= ' GRANT OPTION FOR';
        }

        if (
in_array('ALL PRIVILEGES'$privileges)) {
            
$sql .= ' ALL PRIVILEGES';
        }
        else {
            if (
$type == 'column') {
                
$this->fieldClean($object);
                
$sql .= ' ' join(" (\"{$object}\"), "$privileges);
            }
            else {
                
$sql .= ' ' join(', '$privileges);
            }
        }

        switch (
$type) {
            case 
'column':
                
$sql .= " (\"{$object}\")";
                
$object $table;
            case 
'table':
            case 
'view':
            case 
'sequence':
                
$this->fieldClean($object);
                
$sql .= " ON \"{$f_schema}\".\"{$object}\"";
                break;
            case 
'database':
                
$this->fieldClean($object);
                
$sql .= " ON DATABASE \"{$object}\"";
                break;
            case 
'function':
                
// Function comes in with $object as function OID
                
$fn $this->getFunction($object);
                
$this->fieldClean($fn->fields['proname']);
                
$sql .= " ON FUNCTION \"{$f_schema}\".\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
                break;
            case 
'language':
                
$this->fieldClean($object);
                
$sql .= " ON LANGUAGE \"{$object}\"";
                break;
            case 
'schema':
                
$this->fieldClean($object);
                
$sql .= " ON SCHEMA \"{$object}\"";
                break;
            case 
'tablespace':
                
$this->fieldClean($object);
                
$sql .= " ON TABLESPACE \"{$object}\"";
                break;
            default:
                return -
1;
        }

        
// Dump PUBLIC
        
$first true;
        
$sql .= ($mode == 'GRANT') ? ' TO ' ' FROM ';
        if (
$public) {
            
$sql .= 'PUBLIC';
            
$first false;
        }
        
// Dump users
        
foreach ($usernames as $v) {
            if (
$first) {
                
$sql .= "\"{$v}\"";
                
$first false;
            }
            else {
                
$sql .= ", \"{$v}\"";
            }
        }
        
// Dump groups
        
foreach ($groupnames as $v) {
            if (
$first) {
                
$sql .= "GROUP \"{$v}\"";
                
$first false;
            }
            else {
                
$sql .= ", GROUP \"{$v}\"";
            }
        }

        
// Grant option
        
if ($this->hasGrantOption() && $mode == 'GRANT' && $grantoption) {
            
$sql .= ' WITH GRANT OPTION';
        }

        
// Cascade revoke
        
if ($this->hasGrantOption() && $mode == 'REVOKE' && $cascade) {
            
$sql .= ' CASCADE';
        }

        return 
$this->execute($sql);
    }

    
/**
     * Helper function that computes encypted PostgreSQL passwords
     * @param $username The username
     * @param $password The password
     */
    
function _encryptPassword($username$password) {
        return 
'md5' md5($password $username);
        }

    
// 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,
                    (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\_%$$';
        }

        
$sql .= " ORDER BY spcname";

        return 
$this->selectSet($sql);
    }

    
/**
     * Retrieves a tablespace's information
     * @return A recordset
     */
    
function getTablespace($spcname) {
        
$this->clean($spcname);

        
$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);
    }

    
/**
     * 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);

        
$sql "CREATE TABLESPACE \"{$spcname}\"";

        if (
$spcowner != '') {
            
$this->fieldClean($spcowner);
            
$sql .= " OWNER \"{$spcowner}\"";
    }

        
$sql .= " LOCATION '{$spcloc}'";

        
$status $this->execute($sql);
        if (
$status != 0) return -1;

        if (
$comment != '' && $this->hasSharedComments()) {
            
$status $this->setComment('TABLESPACE',$spcname,'',$comment);
            if (
$status != 0) return -2;
        }

        return 
0;
    }

    
/**
     * 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 owner error
     * @return -3 rename error
     * @return -4 comment error
     */
    
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;

        
// Owner
        
$sql "ALTER TABLESPACE \"{$spcname}\" OWNER TO \"{$owner}\"";
        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
2;
        }

        
// 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;
            }

            
$spcname $name;
        }

        
// Set comment if it has changed
        
if (trim($comment) != '' && $this->hasSharedComments()) {
            
$status $this->setComment('TABLESPACE',$spcname,'',$comment);
            if (
$status != 0) return -4;
        }

        return 
$this->endTransaction();
    }

    
/**
     * 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);
        }

    
// Administration functions

    /**
     * Analyze a database
     * @param $table (optional) The table to analyze
     */
    
function analyzeDB($table '') {
        if (
$table != '') {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($table);

            
$sql "ANALYZE \"{$f_schema}\".\"{$table}\"";
        }
        else
            
$sql "ANALYZE";

        return 
$this->execute($sql);
    }

    
/**
     * Vacuums a database
     * @param $table The table to vacuum
      * @param $analyze If true, also does analyze
     * @param $full If true, selects "full" vacuum
     * @param $freeze If true, selects aggressive "freezing" of tuples
     */
    
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 != '') {
            
$f_schema $this->_schema;
            
$this->fieldClean($f_schema);
            
$this->fieldClean($table);
            
$sql .= " \"{$f_schema}\".\"{$table}\"";
        }

        return 
$this->execute($sql);
    }

    
/**
     * Returns all autovacuum global configuration
     * @return associative array array( param => value, ...)
     */
    
function getAutovacuum() {

        
$_defaults $this->selectSet("SELECT name, setting
            FROM pg_catalog.pg_settings
            WHERE 
                name = 'autovacuum' 
                OR name = 'autovacuum_vacuum_threshold'
                OR name = 'autovacuum_vacuum_scale_factor'
                OR name = 'autovacuum_analyze_threshold'
                OR name = 'autovacuum_analyze_scale_factor'
                OR name = 'autovacuum_vacuum_cost_delay'
                OR name = 'autovacuum_vacuum_cost_limit'
                OR name = 'vacuum_freeze_min_age'
                OR name = 'autovacuum_freeze_max_age'
            "
        
);

        
$ret = array();
        while (!
$_defaults->EOF) {
            
$ret[$_defaults->fields['name']] = $_defaults->fields['setting'];
            
$_defaults->moveNext();
        }

        return 
$ret;
    }

    
/**
     * Returns all available autovacuum per table information.
     * @return A recordset
     */
    
function saveAutovacuum($table$vacenabled$vacthreshold$vacscalefactor$anathresold,
        
$anascalefactor$vaccostdelay$vaccostlimit)
    {    
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);

        
$sql "ALTER TABLE \"{$f_schema}\".\"{$table}\" SET (";

        if (!empty(
$vacenabled)) {
            
$this->clean($vacenabled);
            
$params[] = "autovacuum_enabled='{$vacenabled}'";
        }
        if (!empty(
$vacthreshold)) {
            
$this->clean($vacthreshold);
            
$params[] = "autovacuum_vacuum_threshold='{$vacthreshold}'";
        }
        if (!empty(
$vacscalefactor)) {
            
$this->clean($vacscalefactor);
            
$params[] = "autovacuum_vacuum_scale_factor='{$vacscalefactor}'";
        }
        if (!empty(
$anathresold)) {
            
$this->clean($anathresold);
            
$params[] = "autovacuum_analyze_threshold='{$anathresold}'";
        }
        if (!empty(
$anascalefactor)) {
            
$this->clean($anascalefactor);
            
$params[] = "autovacuum_analyze_scale_factor='{$anascalefactor}'";
        }
        if (!empty(
$vaccostdelay)) {
            
$this->clean($vaccostdelay);
            
$params[] = "autovacuum_vacuum_cost_delay='{$vaccostdelay}'";
        }
        if (!empty(
$vaccostlimit)) {
            
$this->clean($vaccostlimit);
            
$params[] = "autovacuum_vacuum_cost_limit='{$vaccostlimit}'";
        }

        
$sql $sql implode(','$params) . ');';

        return 
$this->execute($sql);
    }
    
    function 
dropAutovacuum($table) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);
        
        return 
$this->execute("
            ALTER TABLE \"
{$f_schema}\".\"{$table}\" RESET (autovacuum_enabled, autovacuum_vacuum_threshold,
                autovacuum_vacuum_scale_factor, autovacuum_analyze_threshold, autovacuum_analyze_scale_factor,
                autovacuum_vacuum_cost_delay, autovacuum_vacuum_cost_limit
            );"
        
);
    }

    
/**
     * 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);
    }

    
/**
     * 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);
    }

    
/**
     * 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 sendSignal($pid$signal) {
        
// Clean
        
$pid = (int)$pid;

        if (
$signal == 'CANCEL'
            
$sql "SELECT pg_catalog.pg_cancel_backend({$pid}) AS val";
        elseif (
$signal == 'KILL')  
            
$sql "SELECT pg_catalog.pg_terminate_backend({$pid}) AS val";
        else    
            return -
1;
        

        
// Execute the query
        
$val $this->selectField($sql'val');

        if (
$val === 'f') return -1;
        elseif (
$val === 't') return 0;
        else return -
1;
    }

    
// Misc functions

    /**
     * Sets the comment for an object in the database
     * @pre All parameters must already be cleaned
     * @param $obj_type One of 'TABLE' | 'COLUMN' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'TYPE' | 'FUNCTION' | 'AGGREGATE'
     * @param $obj_name The name of the object for which to attach a comment.
     * @param $table Name of table that $obj_name belongs to.  Ignored unless $obj_type is 'TABLE' or 'COLUMN'.
     * @param $comment The comment to add.
     * @return 0 success
     */
    
function setComment($obj_type$obj_name$table$comment$basetype NULL) {
        
$sql "COMMENT ON {$obj_type} " ;
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->clean($comment);  // Passing in an already cleaned comment will lead to double escaped data
                                         // So, while counter-intuitive, it is important to not clean comments before
                                         // calling setComment. We will clean it here instead. 
/*
        $this->fieldClean($table);
        $this->fieldClean($obj_name);
*/

        
switch ($obj_type) {
            case 
'TABLE':
                
$sql .= "\"{$f_schema}\".\"{$table}\" IS ";
                break;
            case 
'COLUMN':
                
$sql .= "\"{$f_schema}\".\"{$table}\".\"{$obj_name}\" IS ";
                break;
            case 
'SEQUENCE':
            case 
'VIEW':
            case 
'TEXT SEARCH CONFIGURATION':
            case 
'TEXT SEARCH DICTIONARY':
            case 
'TEXT SEARCH TEMPLATE':
            case 
'TEXT SEARCH PARSER':
            case 
'TYPE':
                
$sql .= "\"{$f_schema}\".";
            case 
'DATABASE':
            case 
'ROLE':
            case 
'SCHEMA':
            case 
'TABLESPACE':
                
$sql .= "\"{$obj_name}\" IS ";
                break;
            case 
'FUNCTION':
                
$sql .= "\"{$f_schema}\".{$obj_name} IS ";
                break;
            case 
'AGGREGATE':
                
$sql .= "\"{$f_schema}\".\"{$obj_name}\" (\"{$basetype}\") IS ";
                break;
            default:
                
// Unknown object type
            
return -1;
        }

        if (
$comment != '')
            
$sql .= "'{$comment}';";
        else
            
$sql .= 'NULL;';

        return 
$this->execute($sql);

    }

    
/**
     * 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);
    }

    
/**
     * A private helper method for executeScript that advances the
     * character by 1.  In psql this is careful to take into account
     * multibyte languages, but we don't at the moment, so this function
     * is someone redundant, since it will always advance by 1
     * @param &$i The current character position in the line
     * @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;
        
$thislen 1;
    }

    
/**
     * 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 (preg_match('/^[$][$]/'$dquote) || preg_match('/^[$][_[: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
     * the PostgreSQL source code.
     * XXX: It does not handle multibyte languages properly.
     * @param $name Entry in $_FILES to use
     * @param $callback (optional) Callback function to call with each query,
                                   its result and line number.
     * @return True for general success, false on any failure.
     */
    
function executeScript($name$callback null) {
        global 
$data;

        
// This whole function isn't very encapsulated, but hey...
        
$conn $data->conn->_connectionID;
        if (!
is_uploaded_file($_FILES[$name]['tmp_name'])) return false;

        
$fd fopen($_FILES[$name]['tmp_name'], 'r');
        if (!
$fd) return false;

        
// Build up each SQL statement, they can be multiline
        
$query_buf null;
        
$query_start 0;
        
$in_quote 0;
        
$in_xcomment 0;
        
$bslash_count 0;
        
$dol_quote null;
        
$paren_level 0;
        
$len 0;
        
$i 0;
        
$prevlen 0;
        
$thislen 0;
        
$lineno 0;

        
// Loop over each line in the file
        
while (!feof($fd)) {
            
$line fgets($fd);
            
$lineno++;

            
// Nothing left on line? Then ignore...
            
if (trim($line) == '') continue;

            
$len strlen($line);
            
$query_start 0;

            
/*
             * Parse line, looking for command separators.
             *
             * The current character is at line[i], the prior character at line[i
             * - prevlen], the next character at line[i + thislen].
             */
            
$prevlen 0;
            
$thislen = ($len 0) ? 0;

            for (
$i 0$i $len$this->advance_1($i$prevlen$thislen)) {

                
/* was the previous character a backslash? */
                
if ($i && substr($line$i $prevlen1) == '\\')
                    
$bslash_count++;
                else
                    
$bslash_count 0;

                
/*
                 * It is important to place the in_* test routines before the
                 * in_* detection routines. i.e. we have to test if we are in
                 * a quote before testing for comments.
                 */

                /* in quote? */
                
if ($in_quote !== 0)
                {
                    
/*
                     * end of quote if matching non-backslashed character.
                     * backslashes don't count for double quotes, though.
                     */
                    
if (substr($line$i1) == $in_quote &&
                        (
$bslash_count == || $in_quote == '"'))
                        
$in_quote 0;
                }

                
/* in or end of $foo$ type quote? */
                
else if ($dol_quote) {
                    if (
strncmp(substr($line$i), $dol_quotestrlen($dol_quote)) == 0) {
                        
$this->advance_1($i$prevlen$thislen);
                        while(
substr($line$i1) != '$')
                            
$this->advance_1($i$prevlen$thislen);
                        
$dol_quote null;
                    }
                }

                
/* start of extended comment? */
                
else if (substr($line$i2) == '/*')
                {
                    
$in_xcomment++;
                    if (
$in_xcomment == 1)
                        
$this->advance_1($i$prevlen$thislen);
                }

                
/* in or end of extended comment? */
                
else if ($in_xcomment)
                {
                    if (
substr($line$i2) == '*/' && !--$in_xcomment)
                        
$this->advance_1($i$prevlen$thislen);
                }

                
/* start of quote? */
                
else if (substr($line$i1) == '\'' || substr($line$i1) == '"') {
                    
$in_quote substr($line$i1);
                }

                
/*
                 * 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);
                    
$this->advance_1($i$prevlen$thislen);
                    while (
substr($line$i1) != '$') {
                        
$this->advance_1($i$prevlen$thislen);
                    }

                }

                
/* single-line comment? truncate line */
                
else if (substr($line$i2) == '--')
                {
                    
$line substr($line0$i); /* remove comment */
                    
break;
                }

                
/* count nested parentheses */
                
else if (substr($line$i1) == '(') {
                    
$paren_level++;
                }

                else if (
substr($line$i1) == ')' && $paren_level 0) {
                    
$paren_level--;
                }

                
/* semicolon? then send query */
                
else if (substr($line$i1) == ';' && !$bslash_count && !$paren_level)
                {
                    
$subline substr(substr($line0$i), $query_start);
                    
/* is there anything else on the line? */
                    
if (strspn($subline" \t\n\r") != strlen($subline))
                    {
                        
/*
                         * 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 */
                        
$query_buf .= $subline;
                        
$query_buf .= ';';

                        
// Execute the query (supporting 4.1.x PHP...). PHP cannot execute
                        // empty queries, unlike libpq
                        
if (function_exists('pg_query'))
                            
$res = @pg_query($conn$query_buf);
                        else
                            
$res = @pg_exec($conn$query_buf);
                        
// Call the callback function for display
                        
if ($callback !== null$callback($query_buf$res$lineno);
                        
// Check for COPY request
                        
if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
                            
while (!feof($fd)) {
                                
$copy fgets($fd32768);
                                
$lineno++;
                                
pg_put_line($conn$copy);
                                if (
$copy == "\\.\n" || $copy == "\\.\r\n") {
                                    
pg_end_copy($conn);
                                    break;
                                }
                            }
                        }
                    }

                    
$query_buf null;
                    
$query_start $i $thislen;
                }

                
/*
                 * keyword or identifier?
                 * 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 (preg_match('/^[_[:alpha:]]$/'substr($line$i1))) {
                    
$sub substr($line$i$thislen);
                    while (
preg_match('/^[\$_A-Za-z0-9]$/'$sub)) {
                        
/* keep going while we still have identifier chars */
                        
$this->advance_1($i$prevlen$thislen);
                        
$sub substr($line$i$thislen);
                    }
                    
// 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. */
            
$subline substr($line$query_start);
            if (
$in_quote || $dol_quote || strspn($subline" \t\n\r") != strlen($subline))
            {
                if (
strlen($query_buf) > 0)
                    
$query_buf .= "\n";
                
$query_buf .= $subline;
            }

            
$line null;

        } 
// end while

        /*
         * Process query at the end of file without a semicolon, so long as
         * it's non-empty.
         */
        
if (strlen($query_buf) > && strspn($query_buf" \t\n\r") != strlen($query_buf))
        {
            
// Execute the query (supporting 4.1.x PHP...)
            
if (function_exists('pg_query'))
                
$res = @pg_query($conn$query_buf);
            else
                
$res = @pg_exec($conn$query_buf);
            
// Call the callback function for display
            
if ($callback !== null$callback($query_buf$res$lineno);
            
// Check for COPY request
            
if (pg_result_status($res) == 4) { // 4 == PGSQL_COPY_FROM
                
while (!feof($fd)) {
                    
$copy fgets($fd32768);
                    
$lineno++;
                    
pg_put_line($conn$copy);
                    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->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 ";
        }

        
$this->fieldClean($table);

        if (isset(
$_REQUEST['schema'])) {
            
$f_schema $_REQUEST['schema'];
            
$this->fieldClean($f_schema);
            
$sql .= "\"{$f_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 (
preg_match('/^[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 (
preg_match('/^[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($querystrlen($query) - 11) == ';')
                    
$query substr($query0strlen($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' && preg_match('/^[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
        
$rs $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} 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) {
        
$f_schema $this->_schema;
        
$this->fieldClean($f_schema);
        
$this->fieldClean($table);

        
$sql "SELECT * FROM \"{$f_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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "SELECT * FROM pg_stat_all_tables 
            WHERE schemaname='
{$c_schema}' AND 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "SELECT * FROM pg_statio_all_tables 
            WHERE schemaname='
{$c_schema}' AND 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "SELECT * FROM pg_stat_all_indexes 
            WHERE schemaname='
{$c_schema}' AND 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) {
        
$c_schema $this->_schema;
        
$this->clean($c_schema);
        
$this->clean($table);

        
$sql "SELECT * FROM pg_statio_all_indexes 
            WHERE schemaname='
{$c_schema}' AND relname='{$table}
            ORDER BY indexrelname"
;

        return 
$this->selectSet($sql);
    }

    
// Capabilities

    
function hasAggregateSortOp() { return true; }
    function 
hasAlterAggregate() { return true; }
    function 
hasAlterColumnType() { return true; }
    function 
hasAlterDatabaseOwner() { return true; }
    function 
hasAlterDatabaseRename() { return true; }
    function 
hasAlterSchema() { return true; }
    function 
hasAlterSchemaOwner() { return true; }
    function 
hasAlterSequenceSchema() { return true; }
    function 
hasAlterSequenceStart() { return true; }
    function 
hasAlterTableSchema() { return true; }
    function 
hasAutovacuum() { return true; }
    function 
hasCreateTableLike() { return true; }
    function 
hasCreateTableLikeWithConstraints() { return true; }
    function 
hasCreateTableLikeWithIndexes() { return true; }
    function 
hasCreateFieldWithConstraints() { return true; }
    function 
hasDisableTriggers() { return true; }
    function 
hasAlterDomains() { return true; }
    function 
hasDomainConstraints() { return true; }
    function 
hasEnumTypes() { return true; }
    function 
hasFTS() { return true; }
    function 
hasFunctionAlterOwner() { return true; }
    function 
hasFunctionAlterSchema() { return true; }
    function 
hasFunctionCosting() { return true; }
    function 
hasFunctionGUC() { return true; }
    function 
hasGrantOption() { return true; }
    function 
hasNamedParams() { return true; }
    function 
hasPrepare() { return true; }
    function 
hasPreparedXacts() { return true; }
    function 
hasReadOnlyQueries() { return true; }
    function 
hasRecluster() { return true; }
    function 
hasRoles() { return true; }
    function 
hasServerAdminFuncs() { return true; }
    function 
hasSharedComments() { return true; }
    function 
hasQueryCancel() { return true; }
    function 
hasTablespaces() { return true; }
    function 
hasUserRename() { return true; }
    function 
hasVirtualTransactionId() { return true; }
    function 
hasAlterDatabase() { return $this->hasAlterDatabaseRename(); }
    function 
hasDatabaseCollation() { return true; }
    function 
hasMagicTypes() { return true; }
    function 
hasQueryKill() { return true; }
    function 
hasConcurrentIndexBuild() { return true; }
    function 
hasForceReindex() { return false; }
    
}
?>

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

:: Make Dir ::
 
[ Read-Only ]
:: Make File ::
 
[ Read-Only ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 2.0 [PHP 7 Update] [25.02.2019] maintained by PinoyWH1Z | C99Shell Github | Generation time: 0.0411 ]--