!C99Shell v.2.1 [PHP 7 Update] [1.12.2019]!

Software: Apache/2.2.8 (Ubuntu) PHP/5.2.4-2ubuntu5.12 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g. PHP/5.2.4-2ubuntu5.12 

uname -a: Linux forum.circlefusion.com 2.6.24-19-server #1 SMP Wed Jun 18 15:18:00 UTC 2008 i686 

uid=33(www-data) gid=33(www-data) groups=33(www-data) 

Safe-mode: OFF (not secure)

/home/doku/axrepos/ax/pgadmin/classes/database/   drwxr-xr-x
Free 11.18 GB of 97.11 GB (11.51%)
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 (140.74 KB)      -rwxr-xr-x
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 $
 */

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

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

class 
Postgres extends ADODB_base {

    
// Major version. MUST be numerically comparable to other versions.
    
var $major_version 7.0;
    
// Array of allowed type alignments
    
var $typAligns = array('char''int2''int4''double');
    
// The default type alignment
    
var $typAlignDef 'int4';
    
// Array of allowed type storage attributes
    
var $typStorages = array('plain''external''extended''main');
    
// The default type storage
    
var $typStorageDef 'plain';
    
// Extra "magic" types
    
var $extraTypes = array('SERIAL');
    
// Array of allowed index types
    
var $typIndexes = array('BTREE''RTREE''GIST''HASH');
    
// Default index type
    
var $typIndexDef 'BTREE';
    
// Array of allowed trigger events
    
var $triggerEvents= array('INSERT''UPDATE''DELETE''INSERT OR UPDATE''INSERT OR DELETE',
        
'DELETE OR UPDATE''INSERT OR DELETE OR UPDATE');
    
// When to execute the trigger
    
var $triggerExecTimes = array('BEFORE''AFTER');
    
// How often to execute the trigger
    
var $triggerFrequency = array('ROW');
    
// Foreign key stuff.  First element MUST be the default.
    
var $fkactions = array('NO ACTION''RESTRICT''CASCADE''SET NULL''SET DEFAULT');
    var 
$fkmatches = array('MATCH SIMPLE''MATCH FULL');
    var 
$fkdeferrable = array('NOT DEFERRABLE''DEFERRABLE');
    var 
$fkinitial = array('INITIALLY IMMEDIATE''INITIALLY DEFERRED');
    
// Function properties
    
var $funcprops = array(array('''ISCACHABLE'));
    var 
$defaultprops = array('');

    
// Last oid assigned to a system object
    
var $_lastSystemOID 18539;
    var 
$_maxNameLen 31;

    
// Name of id column
    
var $id 'oid';

    
// Map of database encoding names to HTTP encoding names.  If a
    // database encoding does not appear in this list, then its HTTP
    // encoding name is the same as its database encoding name.
    
var $codemap = array(
        
'ALT' => 'CP866',
        
'EUC_CN' => 'GB2312',
        
'EUC_JP' => 'EUC-JP',
        
'EUC_KR' => 'EUC-KR',
        
'EUC_TW' => 'EUC-TW',
        
'ISO_8859_5' => 'ISO-8859-5',
        
'ISO_8859_6' => 'ISO-8859-6',
        
'ISO_8859_7' => 'ISO-8859-7',
        
'ISO_8859_8' => 'ISO-8859-8',
        
'JOHAB' => 'CP1361',
        
'KOI8' => 'KOI8-R',
        
'LATIN1' => 'ISO-8859-1',
        
'LATIN2' => 'ISO-8859-2',
        
'LATIN3' => 'ISO-8859-3',
        
'LATIN4' => 'ISO-8859-4',
        
// The following encoding map is a known error in PostgreSQL < 7.2
        // See the constructor for Postgres72.
        
'LATIN5' => 'ISO-8859-5',
        
'LATIN6' => 'ISO-8859-10',
        
'LATIN7' => 'ISO-8859-13',
        
'LATIN8' => 'ISO-8859-14',
        
'LATIN9' => 'ISO-8859-15',
        
'LATIN10' => 'ISO-8859-16',
        
'SQL_ASCII' => 'US-ASCII',
        
'TCVN' => 'CP1258',
        
'UNICODE' => 'UTF-8',
        
'WIN' => 'CP1251',
        
'WIN874' => 'CP874',
        
'WIN1256' => 'CP1256'
    
);

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

    
// List of all legal privileges that can be applied to different types
    // of objects.
    
var $privlist = array(
        
'table' => array('SELECT''INSERT''UPDATE''RULE''ALL'),
        
'view' => array('SELECT''INSERT''UPDATE''RULE''ALL'),
        
'sequence' => array('SELECT''UPDATE''ALL')
    );

    
// List of characters in acl lists and the privileges they
    // refer to.
    
var $privmap = array(
        
'r' => 'SELECT',
        
'w' => 'UPDATE',
        
'a' => 'INSERT',
        
'R' => 'RULE'
    
);

    
// Rule action types
    
var $rule_events = array('SELECT''INSERT''UPDATE''DELETE');

    
// Select operators
    // Operators of type 'i' are 'infix', eg. a = '1'.  Type 'p' means postfix unary, eg. a IS TRUE.
    // 'x' is a bracketed subquery form.  eg. IN (1,2,3)
    
var $selectOps = array('=' => 'i''!=' => 'i''<' => 'i''>' => 'i''<=' => 'i''>=' => 'i''<<' => 'i''>>' => 'i''<<=' => 'i''>>=' => 'i',
                                    
'LIKE' => 'i''NOT LIKE' => 'i''~' => 'i''!~' => 'i''~*' => 'i''!~*' => 'i',
                                    
'IS NULL' => 'p''IS NOT NULL' => 'p''IN' => 'x''NOT IN' => 'x');

    
// Supported join operations for use with view wizard
    
var $joinOps = array('INNER JOIN' => 'INNER JOIN');

    
// Default help URL
    
var $help_base;

    
// Help sub pages
    
var $help_page;

    
// Predefined size types
    
var $predefined_size_types = array('abstime','aclitem','bigserial','boolean','bytea','cid','cidr','circle','date','float4','float8','gtsvector','inet','int2','int4','int8','macaddr','money','oid','path','polygon','refcursor','regclass','regoper','regoperator','regproc','regprocedure','regtype','reltime','serial','smgr','text','tid','tinterval','tsquery','tsvector','varbit','void','xid');

    
/**
     * Constructor
     * @param $conn The database connection
     */
    
function Postgres($conn) {
        
$this->ADODB_base($conn);
    }

    
// Help functions

    /**
     * Fetch a URL (or array of URLs) for a given help page.
     */
    
function getHelp($help) {
        
$this->getHelpPages();

        if (isset(
$this->help_page[$help])) {
            if (
is_array($this->help_page[$help])) {
                
$urls = array();
                foreach (
$this->help_page[$help] as $link) {
                    
$urls[] = $this->help_base $link;
                }
                return 
$urls;
            } else
                return 
$this->help_base $this->help_page[$help];
        } else
            return 
null;
    }

    
/**
     * Initialize help pages and return the full list
     */
    
function getHelpPages() {
        include_once(
'./help/PostgresDoc70.php');
        return 
$this->help_page;
    }

    
// Formatting functions

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

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

    
/**
     * 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 $actions An array of javascript action name to the code to execute on that action
     */
    
function printField($name$value$type$actions = array(),$szExtra="") {
        global 
$lang;

        
// Determine actions string
        
$action_str '';
        foreach (
$actions as $k => $v) {
            
$action_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), "\"{$action_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\"{$action_str} {$szExtra} />\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\"{$action_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\"{$action_str}>\n";
                echo 
htmlspecialchars($value);
                echo 
"</textarea>\n";
                break;
            default:
                echo 
"<input name=\""htmlspecialchars($name), "\" value=\""htmlspecialchars($value), "\" size=\"35\"{$action_str} {$szExtra} />\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;
    }

    
/**
     * Returns the current schema to prepend on object names 
     */
    
function schema() {
        return 
'';
    }

    
// Database functions

    /**
     * Return all database available on the server
     * @return A list of databases, sorted alphabetically
     */
    
function getDatabases($currentdatabase NULL) {
        global 
$conf$misc;

        
$server_info $misc->getServerInfo();

        if (isset(
$conf['owned_only']) && $conf['owned_only'] && !$this->isSuperUser($server_info['username'])) {
            
$username $server_info['username'];
            
$this->clean($username);
            
$clause " AND pu.usename='{$username}'";
        }
        else 
$clause '';

        if (
$currentdatabase != NULL)
            
$orderby "ORDER BY pdb.datname = '{$currentdatabase}' DESC, pdb.datname";
        else
            
$orderby "ORDER BY pdb.datname";

        if (!
$conf['show_system'])
            
$where "AND pdb.datname NOT IN ('template1')";
        else
            
$where '';

        
$sql "SELECT pdb.datname, pu.usename AS datowner, pg_encoding_to_char(encoding) AS datencoding,
                    (SELECT description FROM pg_description pd WHERE pdb.oid=pd.objoid) AS datcomment
                    FROM pg_database pdb, pg_user pu
                    WHERE pdb.datdba = pu.usesysid
                    {$where}
                    {$clause}
                    {$orderby}"
;

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

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

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

    
/**
     * Sets the client encoding
     * @param $encoding The encoding to for the client
     * @return 0 success
     */
    
function setClientEncoding($encoding) {
        return -
99;
    }

    
/**
     * 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 '') {
        
$this->fieldClean($database);
        
$this->clean($encoding);
        
$this->fieldClean($tablespace);
        
$this->fieldClean($comment);

        if (
$encoding == '') {
            
$sql "CREATE DATABASE \"{$database}\"";
        } else {
            
$sql "CREATE DATABASE \"{$database}\" WITH ENCODING='{$encoding}'";
        }

        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;

    }

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

    
// Schema functions

    /**
     * Sets the current working schema.  This is a do nothing method for
     * < 7.3 and is just here for polymorphism's sake.
     * @param $schema The the name of the schema to work in
     * @return 0 success
     */
    
function setSchema($schema) {
        return 
0;
    }

    
// Inheritance functions

    /**
     * Finds the names and schemas of parent tables (in order)
     * @param $table The table to find the parents for
     * @return A recordset
     */
    
function getTableParents($table) {
        
$this->clean($table);

        
$sql "
            SELECT
                NULL AS nspname, relname
            FROM
                pg_class pc, pg_inherits pi
            WHERE
                pc.oid=pi.inhparent
                AND pi.inhrelid = (SELECT oid from pg_class WHERE relname='{$table}')
            ORDER BY
                pi.inhseqno
        "
;

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


    
/**
     * Finds the names and schemas of child tables
     * @param $table The table to find the children for
     * @return A recordset
     */
    
function getTableChildren($table) {
        
$this->clean($table);

        
$sql "
            SELECT
                NULL AS nspname, relname
            FROM
                pg_class pc, pg_inherits pi
            WHERE
                pc.oid=pi.inhrelid
                AND pi.inhparent = (SELECT oid from pg_class WHERE relname='{$table}')
        "
;

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

    
// Table functions

    /**
     * Returns table information
     * @param $table The name of the table
     * @return A recordset
     */
    
function getTable($table) {
        
$this->clean($table);

        
$sql "SELECT pc.relname,
            pg_get_userbyid(pc.relowner) AS relowner,
            (SELECT description FROM pg_description pd
                        WHERE pc.oid=pd.objoid) AS relcomment
            FROM pg_class pc
            WHERE pc.relname='{$table}'"
;

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

    
/**
     * Return all tables in current database
     * @param $all True to fetch all tables, false for just in current schema
     * @return All tables, sorted alphabetically
     */
    
function getTables($all false) {
        global 
$conf;
        if (!
$conf['show_system'] || $all$where "AND c.relname NOT LIKE 'pg@_%' ESCAPE '@' ";
        else 
$where '';

        
$sql "SELECT NULL AS nspname, c.relname,
                    (SELECT usename FROM pg_user u WHERE u.usesysid=c.relowner) AS relowner,
                    (SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS relcomment,
                    reltuples::bigint AS reltuples
                FROM pg_class c
                WHERE c.relkind='r'
                    AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
                    {$where}
                ORDER BY relname"
;
        return 
$this->selectSet($sql);
    }

    
/**
     * Retrieve the attribute definition of a table
     * @param $table The name of the table
     * @param $field (optional) The name of a field to return
     * @return All attributes in order
     */
    
function getTableAttributes($table$field '') {
        
$this->clean($table);
        
$this->clean($field);

        if (
$field == '') {
            
$sql "SELECT
                    a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
                    (SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
                    a.attstorage AS typstorage, false AS attisserial,
                                        (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
                FROM
                    pg_attribute a,
                    pg_class c,
                    pg_type t
                WHERE
                    c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid
                ORDER BY a.attnum"
;
        }
        else {
            
$sql "SELECT
                    a.attname, t.typname as type, t.typname as base_type,
                    a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, -1 AS attstattarget, a.attstorage,
                    (SELECT adsrc FROM pg_attrdef adef WHERE a.attrelid=adef.adrelid AND a.attnum=adef.adnum) AS adsrc,
                    a.attstorage AS typstorage,
                                       (SELECT description FROM pg_description d WHERE d.objoid = a.oid) as comment
                FROM
                    pg_attribute a ,
                    pg_class c,
                    pg_type t
                WHERE
                    c.relname = '{$table}' AND a.attname='{$field}' AND a.attrelid = c.oid AND a.atttypid = t.oid"
;
        }

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

    
/**
     * Checks to see whether or not a table has a unique id column
     * @param $table The table name
     * @return True if it has a unique id, false otherwise
     * @return null error
     */
    
function hasObjectID($table) {
        
// 7.0 and 7.1 always had an oid column
        
return true;
    }

    
/**
     * Returns the current default_with_oids setting
     * @return default_with_oids setting
     */
    
function getDefaultWithOid() {
        
// 8.0 is the first release to have this setting
        // Prior releases don't have this setting... oids always activated
        
return 'on';
    }

    
/**
     * Creates a new table in the database
     * @param $name The name of the table
     * @param $fields The number of fields
     * @param $field An array of field names
     * @param $type An array of field types
     * @param $array An array of '' or '[]' for each type if it's an array or not
     * @param $length An array of field lengths
     * @param $notnull An array of not null
     * @param $default An array of default values
     * @param $withoutoids True if WITHOUT OIDS, false otherwise
     * @param $colcomment An array of comments
     * @param $comment Table comment
     * @param $tablespace The tablespace name ('' means none/default)
      * @param $uniquekey An Array indicating the fields that are unique (those indexes that are set)
      * @param $primarykey An Array indicating the field used for the primarykey (those indexes that are set)
     * @return 0 success
     * @return -1 no fields supplied
     */
    
function createTable($name$fields$field$type$array$length$notnull,
                
$default$withoutoids$colcomment$tblcomment$tablespace,
                
$uniquekey$primarykey) {
        
$this->fieldClean($name);
        
$this->clean($tblcomment);

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

        
$schema $this->schema();
        
        
$found false;
        
$first true;
        
$comment_sql ''//Accumulate comments for the columns
        
$sql "CREATE TABLE {$schema}\"{$name}\" (";
        for (
$i 0$i $fields$i++) {
            
$this->fieldClean($field[$i]);
            
$this->clean($type[$i]);
            
$this->clean($length[$i]);
            
$this->clean($colcomment[$i]);

            
// Skip blank columns - for user convenience
            
if ($field[$i] == '' || $type[$i] == '') continue;
            
// If not the first column, add a comma
            
if (!$first$sql .= ", ";
            else 
$first false;

            switch (
$type[$i]) {
                
// Have to account for weird placing of length for with/without
                // time zone types
                
case 'timestamp with time zone':
                case 
'timestamp without time zone':
                    
$qual substr($type[$i], 9);
                    
$sql .= "\"{$field[$i]}\" timestamp";
                    if (
$length[$i] != ''$sql .= "({$length[$i]})";
                    
$sql .= $qual;
                    break;
                case 
'time with time zone':
                case 
'time without time zone':
                    
$qual substr($type[$i], 4);
                    
$sql .= "\"{$field[$i]}\" time";
                    if (
$length[$i] != ''$sql .= "({$length[$i]})";
                    
$sql .= $qual;
                    break;
                default:
                    
$sql .= "\"{$field[$i]}\" {$type[$i]}";
                    if (
$length[$i] != ''$sql .= "({$length[$i]})";
            }
            
// Add array qualifier if necessary
            
if ($array[$i] == '[]'$sql .= '[]';
            
// Add other qualifiers
            
if (!isset($primarykey[$i])) {
                 if (isset(
$uniquekey[$i])) $sql .= " UNIQUE";
                 if (isset(
$notnull[$i])) $sql .= " NOT NULL";
             }
            if (
$default[$i] != ''$sql .= " DEFAULT {$default[$i]}";

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

            
$found true;
        }

        if (!
$found) return -1;

        
// PRIMARY KEY
         
$primarykeycolumns = array();
         for (
$i 0$i $fields$i++) {
             if (isset(
$primarykey[$i])) {
                 
$primarykeycolumns[] = "\"{$field[$i]}\"";
             }
        }
         if (
count($primarykeycolumns) > 0) {
             
$sql .= ", PRIMARY KEY (" implode(", "$primarykeycolumns) . ")";
         }

        
$sql .= ")";

        
// WITHOUT OIDS
        
if ($this->hasWithoutOIDs() && $withoutoids)
            
$sql .= ' WITHOUT OIDS';

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

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

        if (
$tblcomment != '') {
            
$status $this->setComment('TABLE'''$name$tblcommenttrue);
            if (
$status) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        if (
$comment_sql != '') {
            
$status $this->execute($comment_sql);
            if (
$status) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }
        return 
$this->endTransaction();

    }

    
/**
     * Protected method which alter a table
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
     * @param $tblrs The table recordSet returned by getTable()
     * @param $name The new name for the table
     * @param $owner The new owner for the table
     * @param $schema The new schema for the table
     * @param $comment The comment on the table
     * @param $tablespace The new tablespace for the table ('' means leave as is)
     * @return 0 success
     * @return -3 rename error
     * @return -4 comment error
     * @return -5 owner error
     * @return -6 tablespace error
     * @return -7 schema error
     */
    /* protected */
    
function _alterTable($tblrs$name$owner$schema$comment$tablespace) {

        
$this->fieldClean($name);
        
$this->clean($comment);
        
/* $schema, $owner, $tablespace not supported in pg70 */

        
$table $tblrs->fields['relname'];

        
// Comment
        
$status $this->setComment('TABLE'''$table$comment);
        if (
$status != 0) return -4;

        
// Rename (only if name has changed)
        
if ($name != $table) {
            
$sql "ALTER TABLE \"{$table}\" RENAME TO \"{$name}\"";
            
$status $this->execute($sql);
            if (
$status != 0) return -3;
        }

        return 
0;
    }

    
/**
     * Alter table properties
     * @param $table The name of the table
     * @param $name The new name for the table
     * @param $owner The new owner for the table
     * @param $schema The new schema for the table
     * @param $comment The comment on the table
     * @param $tablespace The new tablespace for the table ('' means leave as is)
     * @return 0 success
     * @return -1 transaction error
     * @return -2 get existing table error
     * @return $this->_alterTable error code
     */
    
function alterTable($table$name$owner$schema$comment$tablespace) {

        
$this->fieldClean($table);
        
$data $this->getTable($table);
        if (
$data->recordCount() != 1)
            return -
2;

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

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

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

        return 
$this->endTransaction();
    }

    
/**
     * Removes a table from the database
     * @param $table The table to drop
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropTable($table$cascade) {
        
$this->fieldClean($table);

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

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

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

        
$sql "DELETE FROM \"{$table}\"";

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

    
/**
     * Renames a table
     * @param $table The table to be renamed
     * @param $newName The new name for the table
     * @return 0 success
     */
    /*XXX FIXME !! NOT USED ANYMORE ??
    function renameTable($table, $newName) {
        $this->fieldClean($table);
        $this->fieldClean($newName);

        $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\"";

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

        
// 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 if we support schemas
        
$schema '';
        if (
$this->hasSchemas()) {
            
$schema "\"{$this->_schema}\".";
            
$sql .= "SET search_path = \"{$this->_schema}\", 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 .= "{$schema}\"{$t->fields['relname']}\";\n";
        
$sql .= "CREATE TABLE {$schema}\"{$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 ($this->hasSchemas() && $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->hasWithoutOIDs()) {
            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 {$schema}\"{$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 {$schema}\"{$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 {$schema}\"{$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 {$schema}\"{$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;
    }

    
/**
     * Given an array of attnums and a relation, returns an array mapping
     * atttribute number to attribute name.  Relation could be a table OR
     * a view.
     * @param $table The table to get attributes for
     * @param $atts An array of attribute numbers
     * @return An array mapping attnum to attname
     * @return -1 $atts must be an array
     * @return -2 wrong number of attributes found
     */
    
function getAttributeNames($table$atts) {
        
$this->clean($table);
        
$this->arrayClean($atts);

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

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

        
$sql "SELECT attnum, attname FROM pg_attribute WHERE attrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') 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;
        }
    }

    
/**
     * 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) {
        
$this->fieldClean($table);
        
$this->fieldClean($column);
        
$this->clean($type);
        
$this->clean($length);
        
$this->clean($comment);

        
$schema $this->schema();

        if (
$length == '')
            
$sql "ALTER TABLE {$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 {$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 {$schema}\"{$table}\" ADD COLUMN \"{$column}\" time({$length}){$qual}";
                    break;
                default:
                    
$sql "ALTER TABLE {$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->hasAlterColumnType()) {
            
// 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();
    }

    
/**
     * Sets default value of a column
     * @param $table The table from which to drop
     * @param $column The column name to set
     * @param $default The new default value
     * @return 0 success
     */
    
function setColumnDefault($table$column$default) {
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$sql "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";

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

    
/**
     * Drops default value of a column
     * @param $table The table from which to drop
     * @param $column The column name to drop default
     * @return 0 success
     */
    
function dropColumnDefault($table$column) {
        
$this->fieldClean($table);
        
$this->fieldClean($column);

        
$sql "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP 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
     * @return -1 attempt to set not null, but column contains nulls
     * @return -2 transaction error
     * @return -3 lock error
     * @return -4 update error
     */
    
function setColumnNull($table$column$state) {
        
$this->fieldClean($table);
        
$this->fieldClean($column);

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

        
// Properly lock the table
        
$sql "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";
        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
3;
        }

        
// Check for existing nulls
        
if (!$state) {
            
$sql "SELECT COUNT(*) AS total FROM \"{$table}\" WHERE \"{$column}\" IS NULL";
            
$result $this->selectField($sql'total');
            if (
$result 0) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        
// Otherwise update the table.  Note the reverse-sensed $state variable
        
$sql "UPDATE pg_attribute SET attnotnull = " . (($state) ? 'false' 'true') . "
                    WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '{$table}')
                    AND attname = '{$column}'"
;

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

        
// Otherwise, close the transaction
        
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) {
        
$this->fieldClean($table);
        
$this->fieldClean($column);
        
$this->fieldClean($newName);

        
$sql "ALTER TABLE \"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";

        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
     * @return -99 not implemented
     */
    
function dropColumn($table$column$cascade) {
        return -
99;
    }

    
/**
     * Alters a column in a table OR view
     * @param $table The table in which the column resides
     * @param $column The column to alter
     * @param $name The new name for the column
     * @param $notnull (boolean) True if not null, false otherwise
     * @param $oldnotnull (boolean) True if column is already not null, false otherwise
     * @param $default The new default for the column
     * @param $olddefault The old default for the column
     * @param $type The new type for the column
     * @param $array True if array type, false otherwise
     * @param $length The optional size of the column (ie. 30 for varchar(30))
     * @param $oldtype The old type for the column
     * @param $comment Comment for the column
     * @return 0 success
     * @return -1 set not null error
     * @return -2 set default error
     * @return -3 rename column error
     * @return -4 comment error
     */
    
function alterColumn($table$column$name$notnull$oldnotnull$default$olddefault,
                                    
$type$length$array$oldtype$comment) {
        
$this->beginTransaction();

        
// @@ NEED TO HANDLE "NESTED" TRANSACTION HERE
        
if ($notnull != $oldnotnull) {
            
$status $this->setColumnNull($table$column, !$notnull);
            if (
$status != 0) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        
// Set default, if it has changed
        
if ($default != $olddefault) {
            if (
$default == '')
                
$status $this->dropColumnDefault($table$column);
            else
                
$status $this->setColumnDefault($table$column$default);

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

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

        
// Parameters must be cleaned for the setComment function.  It's ok to do
        // that here since this is the last time these variables are used.
        
$this->fieldClean($name);
        
$this->fieldClean($table);
        
$this->clean($comment);
        
$status $this->setComment('COLUMN'$name$table$comment);
        if (
$status != 0) {
          
$this->rollbackTransaction();
          return -
4;
        }

        return 
$this->endTransaction();
    }

    
// Row functions

    /**
     * Delete a row from a table
     * @param $table The table from which to delete
     * @param $key An array mapping column => value to delete
     * @return 0 success
     */
    
function deleteRow($table$key) {
        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;
            }

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

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

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

            
// Build clause
            
if (sizeof($vars) > 0) {
                
$schema $this->schema();
                
                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 {$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();
        }
    }

    
/**
     * Adds a new row to a table
     * @param $table The table in which to insert
     * @param $var An array mapping new values for the row
     * @param $nulls An array mapping column => something if it is to be null
     * @param $format An array of the data type (VALUE or EXPRESSION)
     * @param $types An array of field types
     * @return 0 success
     * @return -1 invalid parameters
     */
    
function insertRow($table$vars$nulls$format$types) {

        if (!
is_array($vars) || !is_array($nulls) || !is_array($format)
            || !
is_array($types)) return -1;
        else {
            
$this->fieldClean($table);

            
$schema $this->schema();
        
            
// Build clause
            
if (sizeof($vars) > 0) {
                
$fields '';
                
$values '';
                foreach(
$vars as $key => $value) {
                    
$this->fieldClean($key);

                    
// Handle NULL values
                    
if (isset($nulls[$key])) $tmp 'NULL';
                    else 
$tmp $this->formatValue($types[$key], $format[$key], $value);

                    if (
$fields$fields .= ", \"{$key}\"";
                    else 
$fields "INSERT INTO {$schema}\"{$table}\" (\"{$key}\"";

                    if (
$values$values .= ", {$tmp}";
                    else 
$values ") VALUES ({$tmp}";
                }
                
$sql $fields $values ')';
            }
            return 
$this->execute($sql);
        }
    }

    
/**
     * Returns a recordset of all columns in a table
     * @param $table The name of a table
     * @param $key The associative array holding the key to retrieve
     * @return A recordset
     */
    
function browseRow($table$key) {
        
$this->fieldClean($table);

        
$schema $this->schema();
            
        
$sql "SELECT * FROM {$schema}\"{$table}\"";
        if (
is_array($key) && sizeof($key) > 0) {
            
$sql .= " WHERE true";
            foreach (
$key as $k => $v) {
                
$this->fieldClean($k);
                
$this->clean($v);
                
$sql .= " AND \"{$k}\"='{$v}'";
            }
        }

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

    
/**
     * Get the fields for uniquely identifying a row in a table
     * @param $table The table for which to retrieve the identifier
     * @return An array mapping attribute number to attribute name, empty for no identifiers
     * @return -1 error
     */
    
function getRowIdentifier($table) {
        
$oldtable $table;
        
$this->clean($table);

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

        
// Get the first primary or unique index (sorting primary keys first) that
        // is NOT a partial index.
        
$sql "SELECT indrelid, indkey FROM pg_index WHERE indisunique AND indrelid=(SELECT oid FROM pg_class
                    WHERE relname='{$table}') AND indpred='' AND indproc=0 ORDER BY indisprimary DESC LIMIT 1"
;
        
$rs $this->selectSet($sql);

        
// If none, check for an OID column.  Even though OIDs can be duplicated, the edit and delete row
        // functions check that they're only modiying a single row.  Otherwise, return empty array.
        
if ($rs->recordCount() == 0) {
            
// Check for OID column
            
$temp = array();
            if (
$this->hasObjectID($table)) {
                
$temp = array('oid');
            }
            
$this->endTransaction();
            return 
$temp;
        }
        
// Otherwise find the names of the keys
        
else {
            
$attnames $this->getAttributeNames($oldtableexplode(' '$rs->fields['indkey']));
            if (!
is_array($attnames)) {
                
$this->rollbackTransaction();
                return -
1;
            }
            else {
                
$this->endTransaction();
                return 
$attnames;
            }
        }
    }

    
// Sequence functions

    /**
     * Returns all sequences in the current database
     * @return A recordset
     */
    
function getSequences($all false) {
        
// $all argument is ignored as it makes no difference
        
$sql "SELECT
                    c.relname AS seqname,
                    u.usename AS seqowner,
                    (SELECT description FROM pg_description pd WHERE c.oid=pd.objoid) AS seqcomment
                FROM
                    pg_class c, pg_user u
                WHERE c.relowner=u.usesysid AND c.relkind = 'S' ORDER BY seqname"
;

        return 
$this->selectSet$sql );
    }

    
/**
     * Returns properties of a single sequence
     * @param $sequence Sequence name
     * @return A recordset
     */
    
function getSequence($sequence) {
        
$temp $sequence;
        
// Need both field cleaned and literal cleaned versions
        
$this->fieldClean($sequence);
        
$this->clean($temp);

        
$sql "SELECT '{$sequence}' AS seqname, s.*,
                    (SELECT description FROM pg_description pd WHERE pd.objoid=(SELECT oid FROM pg_class WHERE relname='{$temp}')) AS seqcomment,
                    u.usename AS seqowner
                FROM \"{$sequence}\" AS s, pg_user u, pg_class c
                WHERE
                    c.relowner = u.usesysid AND c.relkind = 'S'
                    AND c.relname = '{$sequence}'"
;

        return 
$this->selectSet$sql );
    }

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

        
$sql "DROP SEQUENCE \"{$sequence}\"";
        if (
$cascade$sql .= " CASCADE";

        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[$this->sqFields['minvalue']];

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

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

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

    
/**
     * Execute nextval on a given sequence
     * @param $sequence Sequence name
     * @return 0 success
     * @return -1 sequence not found
     */
    
function nextvalSequence($sequence) {
        
/* This double-cleaning is deliberate */
        
$this->fieldClean($sequence);
        
$this->clean($sequence);

        
$sql "SELECT NEXTVAL('\"{$sequence}\"')";

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

    
/**
     * Execute setval on a given sequence
     * @param $sequence Sequence name
     * @param $nextvalue The next value
     * @return 0 success
     * @return -1 sequence not found
     */
    
function setvalSequence($sequence$nextvalue) {
        
/* This double-cleaning is deliberate */
        
$this->fieldClean($sequence);
        
$this->clean($sequence);
        
$this->clean($nextvalue);

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

        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) {
        
$this->fieldClean($sequence);
        
$this->clean($increment);
        
$this->clean($minvalue);
        
$this->clean($maxvalue);
        
$this->clean($startvalue);
        
$this->clean($cachevalue);

        
$schema $this->schema();
        
        
$sql "CREATE SEQUENCE {$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 $sequence The sequence name
     * @param $name The new name for the sequence
     * @return 0 success
     */
    
function renameSequence($sequence$name) {
        
$this->fieldClean($name);
        
$this->fieldClean($sequence);

        
$sql "ALTER TABLE \"{$sequence}\" RENAME TO \"{$name}\"";
        return 
$this->execute($sql);
    }

    
/**
     * Protected method which alter a sequence
     * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
     * @param $seqrs The sequence recordSet returned by getSequence()
     * @param $name The new name for the sequence
     * @param $comment The comment on the sequence
     * @param $owner The new owner for the sequence
     * @param $schema The new schema for the sequence
     * @param $increment The increment
     * @param $minvalue The min value
     * @param $maxvalue The max value
     * @param $startvalue The starting value
     * @param $cachevalue The cache value
     * @param $cycledvalue True if cycled, false otherwise
     * @return 0 success
     * @return -3 rename error
     * @return -4 comment error
     * @return -5 owner error
     * @return -7 schema error
     */
    /*protected*/
    
function _alterSequence($seqrs$name$comment$owner$schema$increment,
                
$minvalue$maxvalue$startvalue$cachevalue$cycledvalue) {

        
$sequence $seqrs->fields['seqname'];
        
$this->fieldClean($name);
        
$this->clean($comment);
        
/* $owner, $schema, $increment, $minvalue, $maxvalue,
         * $startvalue, $cachevalue, $cycledvalue not supported in pg70 */

        // Comment
        
$status $this->setComment('SEQUENCE'$sequence''$comment);
        if (
$status != 0)
            return -
4;

        
// Rename (only if name has changed)
        
if ($name != $sequence) {
            
$status $this->renameSequence($sequence$name);
            if (
$status != 0)
                return -
3;
        }

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

        
$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$startvalue$cachevalue$cycledvalue);

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

        return 
$this->endTransaction();
    }

    
// Constraint functions

    /**
     * Returns a list of all constraints on a table
     * @param $table The table to find rules for
     * @return A recordset
     */
    
function getConstraints($table) {
        
$this->clean($table);

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

        
$sql "
            SELECT
                rcname AS conname,
                'CHECK (' || rcsrc || ')' AS consrc,
                'c' AS contype,
                NULL::int2vector AS indkey
            FROM
                pg_relcheck
            WHERE
                rcrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
            UNION ALL
            SELECT
                pc.relname,
                NULL,
                CASE WHEN indisprimary THEN
                    'p'
                ELSE
                    'u'
                END,
                indkey
            FROM
                pg_class pc,
                pg_index pi
            WHERE
                pc.oid=pi.indexrelid
                AND (pi.indisunique OR pi.indisprimary)
                AND pi.indrelid = (SELECT oid FROM pg_class WHERE relname='{$table}')
            ORDER BY
                1
        "
;

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

    
/**
     * Adds a check constraint to a table
     * @param $table The table to which to add the check
     * @param $definition The definition of the check
     * @param $name (optional) The name to give the check, otherwise default name is assigned
     * @return 0 success
     */
    
function addCheckConstraint($table$definition$name '') {
        
$this->fieldClean($table);
        
$this->fieldClean($name);
        
// @@ How the heck do you clean a definition???

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

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

    
/**
     * Drops a check constraint from a table
     * @param $table The table from which to drop the check
     * @param $name The name of the check to be dropped
     * @return 0 success
     * @return -2 transaction error
     * @return -3 lock error
     * @return -4 check drop error
     */
    
function dropCheckConstraint($table$name) {
        
$this->clean($table);
        
$this->clean($name);

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

        
// Properly lock the table
        
$sql "LOCK TABLE \"{$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_class WHERE relname='{$table}') 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_class WHERE relname='{$table}'))
                    WHERE relname='{$table}'"
;
           
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }

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

    
// Constraint functions

    /**
     * Removes a constraint from a relation
     * @param $constraint The constraint to drop
     * @param $relation The relation from which to drop
     * @param $type The type of constraint (c, f, u or p)
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     * @return -99 dropping foreign keys not supported
     */
    
function dropConstraint($constraint$relation$type$cascade) {
        
$this->fieldClean($constraint);
        
$this->fieldClean($relation);

        switch (
$type) {
            case 
'c':
                
// CHECK constraint
                
return $this->dropCheckConstraint($relation$constraint);
                break;
            case 
'p':
            case 
'u':
                
// PRIMARY KEY or UNIQUE constraint
                
return $this->dropIndex($constraint$cascade);
                break;
            case 
'f':
                
// FOREIGN KEY constraint
                
return -99;
        }
    }

    
/**
     * Adds a unique constraint to a table
     * @param $table The table to which to add the unique
     * @param $fields (array) An array of fields over which to add the unique
     * @param $name (optional) The name to give the unique, otherwise default name is assigned
     * @return 0 success
     * @return -1 invalid fields
     */
    
function addUniqueKey($table$fields$name '') {
        if (!
is_array($fields) || sizeof($fields) == 0) return -1;
        
$this->fieldClean($table);
        
$this->fieldArrayClean($fields);
        
$this->fieldClean($name);

        if (
$name != '')
            
$sql "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" join('","'$fields) . "\")";
        else return -
99// Not supported

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

    
/**
     * 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;
        
$this->fieldClean($table);
        
$this->fieldClean($targschema);
        
$this->fieldClean($targtable);
        
$this->fieldArrayClean($sfields);
        
$this->fieldArrayClean($tfields);
        
$this->fieldClean($name);

        
$schema $this->schema();
        
        
$sql "ALTER TABLE {$schema}\"{$table}\" ADD ";
        if (
$name != ''$sql .= "CONSTRAINT \"{$name}\" ";
        
$sql .= "FOREIGN KEY (\"" join('","'$sfields) . "\") ";
        
$sql .= "REFERENCES ";
        
// Target table needs to be fully qualified
        
if ($this->hasSchemas()) {
            
$sql .= "\"{$targschema}\".";
        }
        
$sql .= "\"{$targtable}\"(\"" join('","'$tfields) . "\") ";
        if (
$match != $this->fkmatches[0]) $sql .= " {$match}";
        if (
$upd_action != $this->fkactions[0]) $sql .= " ON UPDATE {$upd_action}";
        if (
$del_action != $this->fkactions[0]) $sql .= " ON DELETE {$del_action}";
        if (
$deferrable != $this->fkdeferrable[0]) $sql .= " {$deferrable}";
        if (
$initially != $this->fkinitial[0]) $sql .= " {$initially}";

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

    
/**
     * 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
     * @return 0 success
     */
    
function addPrimaryKey($table$fields$name '') {
        
// This function can be faked with a unique index and a catalog twiddle, however
        // how do we ensure that it's only used on NOT NULL fields?
        
return -99// Not supported.
    
}

    
/**
     * Finds the foreign keys that refer to the specified table
     * @param $table The table to find referrers for
     * @return A recordset
     */
    
function getReferrers($table) {
        
// In PostgreSQL < 7.3, there is no way to discover foreign keys
        
return -99;
    }

    
// 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, pg_get_indexdef(i.indexrelid) AS inddef,
                obj_description(c.oid, 'pg_index') AS idxcomment
            FROM pg_class c, pg_class c2, pg_index i
            WHERE c.relname = '{$table}' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
        "
;
        if (
$unique$sql .= " AND i.indisunique ";
        
$sql .= " ORDER BY c2.relname";

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

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

        
$schema $this->schema();

        
$sql "CREATE";
        if (
$unique$sql .= " UNIQUE";
        
$sql .= " INDEX \"{$name}\" ON {$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 ($this->hasPartialIndexes() && 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) {
        
$this->fieldClean($index);

        
$sql "DROP INDEX \"{$index}\"";
        if (
$cascade$sql .= " CASCADE";

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

    
/**
     * Clusters an index
     * @param $index The name of the index
     * @param $table The table the index is on
     * @return 0 success
     */
    
function clusterIndex($index$table) {
        
$this->fieldClean($index);
        
$this->fieldClean($table);

        
// We don't bother with a transaction here, as there's no point rolling
        // back an expensive cluster if a cheap analyze fails for whatever reason
        
$sql "CLUSTER \"{$index}\" ON \"{$table}\"";

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

    
// Rule functions

    /**
     * Returns a list of all rules on a table
     * @param $table The table to find rules for
     * @return A recordset
     */
    
function getRules($table) {
        
$this->clean($table);

        
$sql "SELECT
                *
            FROM
                pg_rules
            WHERE
                tablename='{$table}'
            ORDER BY
                rulename
        "
;

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

    
/**
     * Removes a rule from a relation
     * @param $rule The rule to drop
     * @param $relation The relation from which to drop (unused)
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropRule($rule$relation$cascade) {
        
$this->fieldClean($rule);

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

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

    
/**
     * Creates a rule
     * @param $name The name of the new rule
     * @param $event SELECT, INSERT, UPDATE or DELETE
     * @param $table Table on which to create the rule
     * @param $where When to execute the rule, '' indicates always
     * @param $instead True if an INSTEAD rule, false otherwise
     * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
     * @param $action The action to take
     * @param $replace (optional) True to replace existing rule, false otherwise
     * @return 0 success
     * @return -1 invalid event
     */
    
function createRule($name$event$table$where$instead$type$action$replace false) {
        
$this->fieldClean($name);
        
$this->fieldClean($table);
        if (!
in_array($event$this->rule_events)) return -1;

        
$schema $this->schema();

        
$sql "CREATE";
        if (
$replace$sql .= " OR REPLACE";
        
$sql .= " RULE \"{$name}\" AS ON {$event} TO {$schema}\"{$table}\"";
        
// Can't escape WHERE clause
        
if ($where != ''$sql .= " WHERE {$where}";
        
$sql .= " DO";
        if (
$instead$sql .= " INSTEAD";
        if (
$type == 'NOTHING')
            
$sql .= " NOTHING";
        else 
$sql .= " ({$action})";

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

    
/**
     * Edits a rule
     * @param $name The name of the new rule
     * @param $event SELECT, INSERT, UPDATE or DELETE
     * @param $table Table on which to create the rule
     * @param $where When to execute the rule, '' indicates always
     * @param $instead True if an INSTEAD rule, false otherwise
     * @param $type NOTHING for a do nothing rule, SOMETHING to use given action
     * @param $action The action to take
     * @return 0 success
     * @return -1 invalid event
     * @return -2 transaction error
     * @return -3 drop existing rule error
     * @return -4 create new rule error
     */
    
function setRule($name$event$table$where$instead$type$action) {
        
$status $this->beginTransaction();
        if (
$status != 0) return -2;

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

        
$status $this->createRule($name$event$table$where$instead$type$action);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
4;
        }

        
$status $this->endTransaction();
        return (
$status == 0) ? : -2;
    }

    
// View functions

    /**
     * Returns a list of all views in the database
     * @return All views
     */
    
function getViews() {
        global 
$conf;

        if (!
$conf['show_system'])
            
$where " WHERE viewname NOT LIKE 'pg@_%' ESCAPE '@' ";
        else
            
$where '';

        
$sql "SELECT viewname AS relname, viewowner AS relowner, definition AS vwdefinition,
                  (SELECT description FROM pg_description pd, pg_class pc
                   WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
            FROM pg_views v
            {$where}
            ORDER BY relname"
;

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

    
/**
     * Returns all details for a particular view
     * @param $view The name of the view to retrieve
     * @return View info
     */
    
function getView($view) {
        
$this->clean($view);

        
$sql "SELECT viewname AS relname, NULL AS nspname, viewowner AS relowner, definition AS vwdefinition,
              (SELECT description FROM pg_description pd, pg_class pc
                WHERE pc.oid=pd.objoid AND pc.relname=v.viewname) AS relcomment
            FROM pg_views v
            WHERE viewname='{$view}'"
;

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

    
/**
     * Creates a new view.
     * @param $viewname The name of the view to create
     * @param $definition The definition for the new view
     * @param $replace True to replace the view, false otherwise
     * @return 0 success
     */
    
function createView($viewname$definition$replace$comment) {
        
$status $this->beginTransaction();
        if (
$status != 0) return -1;

        
$this->fieldClean($viewname);
        
$this->clean($comment);

        
// Note: $definition not cleaned

        
$sql "CREATE ";
        if (
$replace$sql .= "OR REPLACE ";
        
$sql .= "VIEW "$this->schema() ."\"{$viewname}\" AS {$definition}";

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

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

        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) {
        
$this->fieldClean($viewname);

        
$sql "DROP VIEW "$this->schema() ."\"{$viewname}\"";
        if (
$cascade$sql .= " CASCADE";

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

    
/**
     * Updates a view.  Postgres 7.1 and below don't have CREATE OR REPLACE view,
     * so we do it with a drop and a recreate.
     * @param $viewname The name fo the view to update
     * @param $definition The new definition for the view
     * @return 0 success
     * @return -1 transaction error
     * @return -2 drop view error
     * @return -3 create view error
     * @return -4 comment error
     */
    
function setView($viewname$definition$comment) {
        
$status $this->beginTransaction();
        if (
$status != 0) return -1;

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

        
$status $this->createView($viewname$definitionfalse$comment);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
3;
        }

        
$status $this->endTransaction();
        return (
$status == 0) ? : -1;
    }
    
/**
     * Rename a view
     * @param $view The current view's name
     * @param $name The new view's name
     * @return -1 Failed
     * @return 0 success
     */
    
function renameView($view$name) {
        
$this->fieldClean($name);
        
$this->fieldClean($view);
        
$sql "ALTER TABLE \"{$view}\" RENAME TO \"{$name}\"";
        if (
$this->execute($sql) != 0)
            return -
1;
        return 
0;
    }

     
/**
      * Protected method which alter a view
      * SHOULDN'T BE CALLED OUTSIDE OF A TRANSACTION
      * @param $vwrs The view recordSet returned by getView()
      * @param $name The new name for the view
      * @param $owner The new owner for the view
      * @param $comment The comment on the view
      * @return 0 success
      * @return -3 rename error
      * @return -4 comment error
      * @return -5 owner error
      * @return -6 schema error
      */
    
function _alterView($vwrs$name$owner$schema$comment) {

        
$this->fieldClean($name);
        
$this->clean($comment);

        
$view $vwrs->fields['relname'];

        
// Comment
        
if ($this->setComment('VIEW'$view''$comment) != 0)
            return -
4;

        
// Rename (only if name has changed)
        
if ($name != $view) {
            if (
$this->renameView($view$name) != 0)
            return -
3;
        }

        return 
0;
    }

    
/**
     * Alter table properties
     * @param $table The name of the view
     * @param $name The new name for the view
     * @param $owner The new owner for the view
     * @param $schema The new schema for the view
     * @param $comment The comment on the view
     * @return 0 success
     * @return -1 transaction error
     * @return -2 get existing view error
     * @return $this->_alterView error code
     */
    
function alterView($view$name$owner$schema$comment) {

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

    
// Operator functions

    /**
     * Returns a list of all operators in the database
     * @return All operators
     */
    
function getOperators() {
        global 
$conf;
        if (!
$conf['show_system'])
            
$where "WHERE po.oid > '{$this->_lastSystemOID}'::oid";
        else 
$where  '';

        
$sql "
            SELECT
            po.oid,
                po.oprname,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
                (SELECT description FROM pg_description pd WHERE po.oid=pd.objoid) AS oprcomment
            FROM
                pg_operator po
            {$where}
            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,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,
                (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname,
                po.oprcanhash,
                (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprcom) AS oprcom,
                (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprnegate) AS oprnegate,
                (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprlsortop) AS oprlsortop,
                (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprltcmpop) AS oprltcmpop,
                (SELECT oprname FROM pg_operator po2 WHERE po2.oid=po.oprgtcmpop) AS oprgtcmpop,
                po.oprcode::regproc AS oprcode,
                --(SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprcode) AS oprcode,
                (SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprrest) AS oprrest,
                (SELECT proname FROM pg_proc pp WHERE pp.oid=po.oprjoin) AS oprjoin
            FROM
                pg_operator po
            WHERE
                po.oid='{$operator_oid}'
        "
;

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

    
/**
     * Drops an operator
     * @param $operator_oid The OID of the operator to drop
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropOperator($operator_oid$cascade) {
        
// Function comes in with $object as operator OID
        
$opr $this->getOperator($operator_oid);
        
$this->fieldClean($opr->fields['oprname']);

        
$schema $this->schema();

        
$sql "DROP OPERATOR {$schema}{$opr->fields['oprname']} (";
        
// Quoting or formatting here???
        
if ($opr->fields['oprleftname'] !== null$sql .= $opr->fields['oprleftname'] . ', ';
        else 
$sql .= "NONE, ";
        if (
$opr->fields['oprrightname'] !== null$sql .= $opr->fields['oprrightname'] . ')';
        else 
$sql .= "NONE)";

        if (
$cascade$sql .= " CASCADE";

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

    
// User functions

    /**
     * Changes a user's password
     * @param $username The username
     * @param $password The new password
     * @return 0 success
     */
    
function changePassword($username$password) {
        
$this->fieldClean($username);
        
$this->clean($password);

        
$sql "ALTER USER \"{$username}\" WITH PASSWORD '{$password}'";

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

    
/**
     * Returns all users in the database cluster
     * @return All users
     */
    
function getUsers() {
        
$sql "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
        if (
$this->hasUserSessionDefaults()) $sql .= ", useconfig";
        
$sql .= " FROM pg_user ORDER BY usename";

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

    
/**
     * Returns information about a single user
     * @param $username The username of the user to retrieve
     * @return The user's data
     */
    
function getUser($username) {
        
$this->clean($username);

        
$sql "SELECT usename, usesuper, usecreatedb, valuntil AS useexpires";
        if (
$this->hasUserSessionDefaults()) $sql .= ", useconfig";
        
$sql .= " FROM pg_user WHERE usename='{$username}'";

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

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

    
/**
     * 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) {
        
$this->fieldClean($username);
        
$this->clean($password);
        
$this->clean($expiry);
        
$this->fieldArrayClean($groups);

        
$sql "CREATE USER \"{$username}\"";
        if (
$password != ''$sql .= " WITH PASSWORD '{$password}'";
        
$sql .= ($createdb) ? ' CREATEDB' ' NOCREATEDB';
        
$sql .= ($createuser) ? ' CREATEUSER' ' NOCREATEUSER';
        if (
is_array($groups) && sizeof($groups) > 0$sql .= " IN GROUP \"" join('", "'$groups) . "\"";
        if (
$expiry != ''$sql .= " VALID UNTIL '{$expiry}'";
        else 
$sql .= " VALID UNTIL 'infinity'";

        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) {
        
$this->fieldClean($username);
        
$this->clean($password);
        
$this->clean($expiry);

        
$sql "ALTER USER \"{$username}\"";
        if (
$password != ''$sql .= " WITH PASSWORD '{$password}'";
        
$sql .= ($createdb) ? ' CREATEDB' ' NOCREATEDB';
        
$sql .= ($createuser) ? ' CREATEUSER' ' NOCREATEUSER';
        if (
$expiry != ''$sql .= " VALID UNTIL '{$expiry}'";
        else 
$sql .= " VALID UNTIL 'infinity'";

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

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

    
// Group functions

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

    
/**
     * Returns 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 grolist FROM pg_group WHERE groname = '{$groname}'";

        
$grodata $this->selectSet($sql);
        if (
$grodata->fields['grolist'] !== null && $grodata->fields['grolist'] != '{}') {
            
$members $grodata->fields['grolist'];
            
$members preg_replace("/\{|\}/","",$members);
            
$this->clean($members);

            
$sql "SELECT usename FROM pg_user WHERE usesysid IN ({$members}) ORDER BY usename";
        }
        else 
$sql "SELECT usename FROM pg_user WHERE false";

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

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

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

    
// Type functions

    /**
     * Returns a list of all types in the database
     * @param $all If true, will find all available functions, if false just those in search path
     * @param $tabletypes If true, will include table types
     * @param $domains Ignored
     * @return A recordet
     */
    
function getTypes($all false$tabletypes false$domains false) {
        global 
$conf;

        if (
$all || $conf['show_system']) {
            
$where '';
        } else {
            
$where "AND pt.oid > '{$this->_lastSystemOID}'::oid";
        }
        
// Never show system table types
        
$where2 "AND c.oid > '{$this->_lastSystemOID}'::oid";

        
// Create type filter
        
$tqry "'c'";
        if (
$tabletypes)
            
$tqry .= ", 'r', 'v'";

        
$sql "SELECT
                pt.typname AS basename,
                pt.typname,
                pu.usename AS typowner,
                (SELECT description FROM pg_description pd WHERE pt.oid=pd.objoid) AS typcomment
            FROM
                pg_type pt,
                pg_user pu
            WHERE
                pt.typowner = pu.usesysid
                AND (pt.typrelid = 0 OR (SELECT c.relkind IN ({$tqry}) FROM pg_class c WHERE c.oid = pt.typrelid {$where2}))
                AND typname !~ '^_'
                {$where}
            ORDER BY typname
        "
;

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

    
/**
     * Returns all details for a particular type
     * @param $typname The name of the view to retrieve
     * @return Type info
     */
    
function getType($typname) {
        
$this->clean($typname);

        
$sql "SELECT typtype, typbyval, typname, typinput AS typin, typoutput AS typout, typlen, typalign
            FROM pg_type WHERE typname='{$typname}'"
;

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

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

        
$schema $this->schema();

        
$sql "
            CREATE TYPE {$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) {
        
$this->fieldClean($typname);

        
$sql "DROP TYPE \"{$typname}\"";
        if (
$cascade$sql .= " CASCADE";

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

    
// Trigger functions

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

        
// Table name
        
$tgdef .= " ON ";
        if(
$this->hasSchemas())
            
$tgdef .= "\"{$this->_schema}\".";
        
$tgdef .= "\"{$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;
    }

    
/**
     * Grabs a list of triggers on a table
     * @param $table The name of a table whose triggers to retrieve
     * @return A recordset
     */
    
function getTriggers($table '') {
        
$this->clean($table);

        
// We include constraint triggers
        
$sql "SELECT t.tgname, t.tgisconstraint, t.tgdeferrable, t.tginitdeferred, t.tgtype,
            t.tgargs, t.tgnargs, t.tgconstrrelid,
            (SELECT relname FROM pg_class c2 WHERE c2.oid=t.tgconstrrelid) AS tgconstrrelname,
            (SELECT proname FROM pg_proc p WHERE t.tgfoid=p.oid) AS tgfname,
            c.relname, NULL AS tgdef
            FROM pg_trigger t, pg_class c
            WHERE t.tgrelid=c.oid
            AND c.relname='{$table}'"
;

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

    
/**
     * Creates a trigger
     * @param $tgname The name of the trigger to create
     * @param $table The name of the table
     * @param $tgproc The function to execute
     * @param $tgtime BEFORE or AFTER
     * @param $tgevent Event
     * @param $tgargs The function arguments
     * @return 0 success
     */
    
function createTrigger($tgname$table$tgproc$tgtime$tgevent$tgfrequency$tgargs) {
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);
        
$this->fieldClean($tgproc);

        
/* No Statement Level Triggers in PostgreSQL (by now) */
        
$sql "CREATE TRIGGER \"{$tgname}\" {$tgtime}
                {$tgevent} ON \"{$table}\"
                FOR EACH {$tgfrequency} EXECUTE PROCEDURE \"{$tgproc}\"({$tgargs})"
;

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

    
/**
     * Drops a trigger
     * @param $tgname The name of the trigger to drop
     * @param $table The table from which to drop the trigger
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropTrigger($tgname$table$cascade) {
        
$this->fieldClean($tgname);
        
$this->fieldClean($table);

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

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

    
// Privilege functions

    /**
     * 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';
            elseif (
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. relation, view or sequence)
     * @return Privileges array
     * @return -1 invalid type
     * @return -2 object not found
     * @return -3 unknown privilege type
     */
    
function getPrivileges($object$type) {
        
$this->clean($object);

        switch (
$type) {
            case 
'table':
            case 
'view':
            case 
'sequence':
                
$sql "SELECT relacl AS acl FROM pg_class WHERE relname='{$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
     * @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) {
        
$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;

        
$schema $this->schema();
        
$sql $mode;

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

        if (
in_array('ALL PRIVILEGES'$privileges))
            
$sql .= " ALL PRIVILEGES ON";
        else
            
$sql .= " " join(', '$privileges) . " ON";
        switch (
$type) {
            case 
'table':
            case 
'view':
            case 
'sequence':
                
$this->fieldClean($object);
                
$sql .= " {$schema}\"{$object}\"";
                break;
            case 
'database':
                
$this->fieldClean($object);
                
$sql .= " DATABASE \"{$object}\"";
                break;
            case 
'function':
                
// Function comes in with $object as function OID
                
$fn $this->getFunction($object);
                
$this->fieldClean($fn->fields['proname']);
                
$sql .= " FUNCTION {$schema}\"{$fn->fields['proname']}\"({$fn->fields['proarguments']})";
                break;
            case 
'language':
                
$this->fieldClean($object);
                
$sql .= " LANGUAGE \"{$object}\"";
                break;
            case 
'schema':
                
$this->fieldClean($object);
                
$sql .= " SCHEMA \"{$object}\"";
                break;
            case 
'tablespace':
                
$this->fieldClean($object);
                
$sql .= " 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);
    }

    
// Administration functions

    /**
     * Vacuums a database
     * @param $table The table to vacuum
      * @param $analyze If true, also does analyze
     * @param $full If true, selects "full" vacuum (PostgreSQL >= 7.2)
     * @param $freeze If true, selects aggressive "freezing" of tuples (PostgreSQL >= 7.2)
     */
    
function vacuumDB($table ''$analyze false$full false$freeze false) {
        
$sql "VACUUM";
        if (
$analyze$sql .= " ANALYZE";
        if (
$table != '') {
            
$this->fieldClean($table);
            
$sql .= " \"{$table}\"";
        }

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

    
/**
     * Analyze a database
     * @param $table (optional) The table to analyze
     */
    
function analyzeDB($table '') {
        if (
$table != '') {
            
$this->fieldClean($table);
            
$sql "VACUUM ANALYZE \"{$table}\"";
        }
        else
            
$sql "VACUUM ANALYZE";

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

    
/**
     * Rebuild indexes
     * @param $type 'DATABASE' or 'TABLE' or 'INDEX'
     * @param $name The name of the specific database, table, or index to be reindexed
     * @param $force If true, recreates indexes forcedly in PostgreSQL 7.0-7.1, forces rebuild of system indexes in 7.2-7.3, ignored in >=7.4
     */
    
function reindex($type$name$force false) {
        
$this->fieldClean($name);
        switch(
$type) {
            case 
'DATABASE':
            case 
'TABLE':
            case 
'INDEX':
                
$sql "REINDEX {$type} \"{$name}\"";
                if (
$force$sql .= ' FORCE';
                break;
            default:
                return -
1;
        }

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

    
// Function functions

    /**
     * Returns a list of all functions in the database
      * @param $all If true, will find all available functions, if false just userland ones
     * @return All functions
     */
    
function getFunctions($all false) {
        global 
$conf;

        if (
$all || $conf['show_system'])
            
$where '';
        else
            
$where "AND pc.oid > '{$this->_lastSystemOID}'::oid";

        
$sql =     "SELECT
                pc.oid AS prooid,
                proname,
                proretset,
                pt.typname AS proresult,
                pl.lanname AS prolanguage,
                oidvectortypes(pc.proargtypes) AS proarguments,
                (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
                proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
                CASE WHEN proretset THEN 'setof '::text ELSE '' END || pt.typname AS proreturns,
                usename as proowner
            FROM
                pg_proc pc, pg_user pu, pg_type pt, pg_language pl
            WHERE
                pc.proowner = pu.usesysid
                AND pc.prorettype = pt.oid
                AND pc.prolang = pl.oid
                {$where}
            UNION
            SELECT
                pc.oid AS prooid,
                proname,
                proretset,
                'opaque' AS proresult,
                pl.lanname AS prolanguage,
                oidvectortypes(pc.proargtypes) AS proarguments,
                (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment,
                proname || ' (' || oidvectortypes(pc.proargtypes) || ')' AS proproto,
                CASE WHEN proretset THEN 'setof '::text ELSE '' END || 'opaque' AS proreturns,
                usename as proowner
            FROM
                pg_proc pc, pg_user pu, pg_type pt, pg_language pl
            WHERE
                pc.proowner = pu.usesysid
                AND pc.prorettype = 0
                AND pc.prolang = pl.oid
                {$where}
            ORDER BY
                proname, proresult
            "
;

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

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

    
/**
     * Returns all details for a particular function
     * @param $function_oid The OID of the function to retrieve
     * @return Function info
     */
    
function getFunction($function_oid) {
        
$this->clean($function_oid);

        
$sql "SELECT
                    pc.oid AS prooid,
                    proname,
                    lanname AS prolanguage,
                    pt.typname AS proresult,
                    prosrc,
                    probin,
                    proretset,
                    proiscachable,
                    oidvectortypes(pc.proargtypes) AS proarguments,
                    (SELECT description FROM pg_description pd WHERE pc.oid=pd.objoid) AS procomment
                FROM
                    pg_proc pc, pg_language pl, pg_type pt
                WHERE
                    pc.oid = '$function_oid'::oid
                    AND pc.prolang = pl.oid
                    AND pc.prorettype = pt.oid
                "
;

        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();

        
// Cachable
        
$f['proiscachable'] = $this->phpBool($f['proiscachable']);
        if (
$f['proiscachable'])
            
$temp[] = 'ISCACHABLE';
        else
            
$temp[] = '';

        return 
$temp;
    }

    
/**
     * Updates a function.  Postgres 7.1 doesn't have CREATE OR REPLACE function,
     * so we do it with a drop and a recreate.
     * @param $function_oid The OID of the function
     * @param $funcname The name of the function to create
     * @param $newname The new name for the function
     * @param $args The array of argument types
     * @param $returns The return type
     * @param $definition The definition for the new function
     * @param $language The language the function is written for
     * @param $flags An array of optional flags
     * @param $setof True if returns a set, false otherwise
     * @param $comment The comment on the function
     * @return 0 success
     * @return -1 transaction error
     * @return -2 drop function error
     * @return -3 create function error
     * @return -4 comment error
     */
    
function setFunction($function_oid$funcname$newname$args$returns$definition$language$flags$setof$comment) {
        
$status $this->beginTransaction();
        if (
$status != 0) return -1;

        
// Drop existing function
        
$status $this->dropFunction($function_oidfalse);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
2;
        }

        
// Create function with new name
        
$status $this->createFunction($newname$args$returns$definition$language$flags$setoffalse);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
3;
        }

        
// Comment on the function
        
$this->fieldClean($newname);
        
$this->clean($comment);

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

        
$status $this->endTransaction();
        return (
$status == 0) ? : -1;
    }

    
/**
     * Creates a new function.
     * @param $funcname The name of the function to create
     * @param $args A comma separated string of types
     * @param $returns The return type
     * @param $definition The definition for the new function
     * @param $language The language the function is written for
     * @param $flags An array of optional flags
     * @param $setof True if it returns a set, false otherwise
     * @param $rows number of rows planner should estimate will be returned
     * @param $cost cost the planner should use in the function execution step
     * @param $replace (optional) True if OR REPLACE, false for normal
     * @return 0 success
     */
    
function createFunction($funcname$args$returns$definition$language$flags$setof$cost$rows$replace false) {
        
$this->fieldClean($funcname);
        
$this->clean($args);
        
$this->clean($language);
        
$this->arrayClean($flags);

        
$sql "CREATE";
        if (
$replace$sql .= " OR REPLACE";
        
$sql .= " FUNCTION \"{$funcname}\" (";

        if (
$args != '')
            
$sql .= $args;

        
// For some reason, the returns field cannot have quotes...
        
$sql .= ") RETURNS ";
        if (
$setof$sql .= "SETOF ";
        
$sql .= "{$returns} AS ";

        if (
is_array($definition)) {
            
$this->arrayClean($definition);
            
$sql .= "'" $definition[0] . "'";
            if (
$definition[1]) {
                
$sql .= ",'" $definition[1] . "'";
            }
        } else {
            
$this->clean($definition);
            
$sql .= "'" $definition "'";
        }

        
$sql .= " LANGUAGE '{$language}'";

        
// Add flags
        
$first true;
        foreach (
$flags as  $v) {
            
// Skip default flags
            
if ($v == '') continue;
            elseif (
$first) {
                
$sql .= " WITH ({$v}";
                
$first false;
            }
            else {
                
$sql .= ", {$v}";
            }
        }
        
// Close off WITH clause if necessary
        
if (!$first$sql .= ")";

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

    
/**
     * Drops a function.
     * @param $function_oid The OID of the function to drop
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropFunction($function_oid$cascade) {
        
// Function comes in with $object as function OID
        
$fn $this->getFunction($function_oid);
        
$this->fieldClean($fn->fields['proname']);

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

        return 
$this->execute($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::regproc AS lanplcallf
            FROM
                pg_language
            {$where}
            ORDER BY
                lanname
        "
;

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

    
// Aggregate functions

    /**
     * Gets all aggregates
     * @return A recordset
     */
    
function getAggregates() {
        global 
$conf;

        if (
$conf['show_system'])
            
$where '';
        else
            
$where "WHERE a.oid > '{$this->_lastSystemOID}'::oid";

        
$sql "
            SELECT
                a.aggname AS proname,
                CASE a.aggbasetype
                    WHEN 0 THEN NULL
                    ELSE (SELECT typname FROM pg_type t WHERE t.oid=a.aggbasetype)
                END AS proargtypes,
                (SELECT description FROM pg_description pd WHERE a.oid=pd.objoid) AS aggcomment
            FROM
                pg_aggregate a
            {$where}
            ORDER BY
                1, 2;
        "
;

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

    
/**
     * Gets all information for an aggregate
     * @param $name The name of the aggregate
     * @param $basetype The input data type of the aggregate
     * @return A recordset
     */
    
function getAggregate($name$basetype) {
        
$this->fieldclean($name);
        
$this->fieldclean($basetype);

        
$sql "
            SELECT a.aggname AS proname,
                CASE a.aggbasetype
                    WHEN 0 THEN NULL
                    ELSE format_type(a.aggbasetype, NULL)
                  END AS proargtypes,
                  a.aggtransfn, format_type(a.aggtranstype, NULL) AS aggstype, a.aggfinalfn, a.agginitval, u.usename,
                  obj_description(a.oid, 'pg_aggregate') AS aggrcomment
            FROM pg_user u, pg_aggregate a
            WHERE a.aggowner=u.usesysid
                AND a.aggname='" 
$name "'
                AND CASE a.aggbasetype
                    WHEN  0 THEN ''
                    ELSE format_type(a.aggbasetype, NULL)
                    END ='" 
$basetype "'";

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

    
/**
     * Creates a new aggregate in the database
     * @param $name The name of the aggregate
     * @param $basetype The input data type of the aggregate
     * @param $sfunc The name of the state transition function for the aggregate
     * @param $stype The data type for the aggregate's state value
     * @param $ffunc The name of the final function for the aggregate
     * @param $initcond The initial setting for the state value
     * @param $sortop The sort operator for the aggregate
     * @param $comment Aggregate comment
     * @return 0 success
     * @return -1 error
     */
    
function createAggregate($name$basetype$sfunc$stype$ffunc$initcond$sortop$comment) {
        
$this->fieldClean($name);
        
$this->fieldClean($basetype);
        
$this->fieldClean($sfunc);
        
$this->fieldClean($stype);
        
$this->fieldClean($ffunc);
        
$this->fieldClean($initcond);
        
$this->fieldClean($sortop);
        
$this->clean($comment);

        
$this->beginTransaction();

        
$schema $this->schema();
        
$sql "CREATE AGGREGATE {$schema}\"{$name}\" (BASETYPE = \"{$basetype}\", SFUNC = \"{$sfunc}\", STYPE = \"{$stype}\"";
        if(
trim($ffunc) != ''$sql .= ", FINALFUNC = \"{$ffunc}\"";
        if(
trim($initcond) != ''$sql .= ", INITCOND = \"{$initcond}\"";
        if(
trim($sortop) != ''$sql .= ", SORTOP = \"{$sortop}\"";
        
$sql .= ")";

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

        if (
trim($comment) != '') {
            
$status $this->setComment('AGGREGATE'$name''$comment$basetype);
            if (
$status) {
                
$this->rollbackTransaction();
                return -
1;
            }
        }

        return 
$this->endTransaction();
    }

    
/**
     * Removes an aggregate function from the database
     * @param $aggrname The name of the aggregate
     * @param $aggrtype The input data type of the aggregate
     * @param $cascade True to cascade drop, false to restrict
     * @return 0 success
     */
    
function dropAggregate($aggrname$aggrtype$cascade) {
        
$this->fieldClean($aggrname);
        
$this->fieldClean($aggrtype);

        
$sql "DROP AGGREGATE \"{$aggrname}\" (\"{$aggrtype}\")";
        if (
$cascade$sql .= " CASCADE";

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

    
// Operator Class functions

    /**
     * Gets all opclasses
     * @return A recordset
     */
    
function getOpClasses() {
        global 
$conf;

        if (
$conf['show_system'])
            
$where '';
        else
            
$where "AND po.oid > '{$this->_lastSystemOID}'::oid";

        
$sql "
            SELECT DISTINCT
                pa.amname,
                po.opcname,
                (SELECT typname FROM pg_type t WHERE t.oid=opcdeftype) AS opcintype,
                TRUE AS opcdefault,
                NULL::text AS opccomment
            FROM
                pg_opclass po, pg_am pa, pg_amop pam
            WHERE
                pam.amopid=pa.oid
                AND pam.amopclaid=po.oid
                {$where}
            ORDER BY 1,2
        "
;

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

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

        
$schema $this->schema();

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

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

        return 
$this->execute($sql);

    }

    
/**
     * Returns the SQL for changing the current user
     * @param $user The user to change to
     * @return The SQL
     */
    
function getChangeUserSQL($user) {
        
$this->fieldClean($user);
        return 
"\\connect - \"{$user}\"";
    }

    
/**
     * Sets up the data object for a dump.  eg. Starts the appropriate
     * transaction, sets variables, etc.
     * @return 0 success
     */
    
function beginDump() {
        
// Begin serializable transaction (to dump consistent data)
        
$status $this->beginTransaction();
        if (
$status != 0) return -1;

        
// Set serializable
        
$sql "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE";
        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        
// Set datestyle to ISO
        
$sql "SET DATESTYLE = ISO";
        
$status $this->execute($sql);
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }
    }

    
/**
     * Ends the data object for a dump.
     * @return 0 success
     */
    
function endDump() {
        return 
$this->endTransaction();
    }

    
/**
     * Generates the SQL for the 'select' function
     * @param $table The table from which to select
     * @param $show An array of columns to show.  Empty array means all columns.
     * @param $values An array mapping columns to values
     * @param $ops An array of the operators to use
     * @param $orderby (optional) An array of column numbers or names (one based)
     *        mapped to sort direction (asc or desc or '' or null) to order by
     * @return The SQL query
     */
    
function getSelectSQL($table$show$values$ops$orderby = array()) {
        
$this->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 (
$this->hasSchemas() && isset($_REQUEST['schema'])) {
            
$this->fieldClean($_REQUEST['schema']);
            
$sql .= "\"{$_REQUEST['schema']}\".";
        }
        
$sql .= "\"{$table}\"";

        
// If we have values specified, add them to the WHERE clause
        
$first true;
        if (
is_array($values) && sizeof($values) > 0) {
            foreach (
$values as $k => $v) {
                if (
$v != '' || $this->selectOps[$ops[$k]] == 'p') {
                    
$this->fieldClean($k);
                    if (
$first) {
                        
$sql .= " WHERE ";
                        
$first false;
                    } else {
                        
$sql .= " AND ";
                    }
                    
// Different query format depending on operator type
                    
switch ($this->selectOps[$ops[$k]]) {
                        case 
'i':
                            
// Only clean the field for the inline case
                            // this is because (x), subqueries need to
                            // to allow 'a','b' as input.
                            
$this->clean($v);
                            
$sql .= "\"{$k}\" {$ops[$k]} '{$v}'";
                            break;
                        case 
'p':
                            
$sql .= "\"{$k}\" {$ops[$k]}";
                            break;
                        case 
'x':
                            
$sql .= "\"{$k}\" {$ops[$k]} ({$v})";
                            break;
                        case 
't':
                            
$sql .= "\"{$k}\" {$ops[$k]}('{$v}')";
                            break;
                        default:
                            
// Shouldn't happen
                    
}
                }
            }
        }

        
// ORDER BY
        
if (is_array($orderby) && sizeof($orderby) > 0) {
            
$sql .= " ORDER BY ";
            
$first true;
            foreach (
$orderby as $k => $v) {
                if (
$first$first false;
                else 
$sql .= ', ';
                if (
preg_match('/^[0-9]+$/'$k)) {
                    
$sql .= $k;
                }
                else {
                    
$this->fieldClean($k);
                    
$sql .= '"' $k '"';
                }
                if (
strtoupper($v) == 'DESC'$sql .= " DESC";
            }
        }

        return 
$sql;
    }

    
/**
     * Finds the number of rows that would be returned by a
     * query.
     * @param $query The SQL query
     * @param $count The count query
     * @return The count of rows
     * @return -1 error
     */
    
function browseQueryCount($query$count) {
        
// Count the number of rows
        
$rs $this->selectSet($query);
        if (!
is_object($rs)) {
            return -
1;
        }

        return 
$rs->recordCount();
    }

    
/**
     * Returns a recordset of all columns in a query.  Supports paging.
     * @param $type Either 'QUERY' if it is an SQL query, or 'TABLE' if it is a table identifier,
     *              or 'SELECT" if it's a select query
     * @param $table The base table of the query.  NULL for no table.
     * @param $query The query that is being executed.  NULL for no query.
     * @param $sortkey The column number to sort by, or '' or null for no sorting
     * @param $sortdir The direction in which to sort the specified column ('asc' or 'desc')
     * @param $page The page of the relation to retrieve
     * @param $page_size The number of rows per page
     * @param &$max_pages (return-by-ref) The max number of pages in the relation
     * @return A recordset on success
     * @return -1 transaction error
     * @return -2 counting error
     * @return -3 page or page_size invalid
     * @return -4 unknown type
     * @return -5 failed setting transaction read only
     */
    
function browseQuery($type$table$query$sortkey$sortdir$page$page_size, &$max_pages) {
        
// Check that we're not going to divide by zero
        
if (!is_numeric($page_size) || $page_size != (int)$page_size || $page_size <= 0) return -3;

        
// If $type is TABLE, then generate the query
        
switch ($type) {
            case 
'TABLE':
                if (
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
        
if ($this->hasFullSubqueries())
            
$rs $this->selectSet("SELECT * FROM ({$query}) AS sub {$orderby} LIMIT {$page_size} OFFSET " . ($page 1) * $page_size);
        else
            
$rs $this->selectSet("{$query} LIMIT {$page_size} OFFSET " . ($page 1) * $page_size);
        
$status $this->endTransaction();
        if (
$status != 0) {
            
$this->rollbackTransaction();
            return -
1;
        }

        return 
$rs;
    }

    
/**
     * Returns a recordset of all columns in a relation.  Used for data export.
     * @@ Note: Really needs to use a cursor
     * @param $relation The name of a relation
     * @return A recordset on success
     * @return -1 Failed to set datestyle
     */
    
function dumpRelation($relation$oids) {
        
$this->fieldClean($relation);

        
// Actually retrieve the rows
        
if ($oids$oid_str $this->id ', ';
        else 
$oid_str '';

        return 
$this->selectSet("SELECT {$oid_str}* FROM \"{$relation}\"");
    }

    
/**
     * Searches all system catalogs to find objects that match a certain name.
     * @param $term The search term
     * @param $filter The object type to restrict to ('' means no restriction)
     * @return A recordset
     */
    
function findObject($term$filter) {
        global 
$conf;

        
// Escape search term for ~* match
        
$special = array('.''*''^''$'':''?''+'',''=''!''['']''('')''{''}''<''>''-''\\');
        foreach (
$special as $v) {
            
$term str_replace($v"\\{$v}"$term);
        }
        
$this->clean($term);
        
$this->clean($filter);

        
// Build SQL, excluding system relations as necessary
        // Relations
        
$case_clause "CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'VIEW'::VARCHAR ELSE 'TABLE'::VARCHAR END) WHEN relkind='v' THEN 'VIEW'::VARCHAR WHEN relkind='S' THEN 'SEQUENCE'::VARCHAR END";
        
$sql "
            SELECT {$case_clause} AS type,
                pc.oid, NULL::VARCHAR AS schemaname, NULL::VARCHAR AS relname, pc.relname AS name FROM pg_class pc
                WHERE relkind IN ('r', 'v', 'S') AND relname ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter == 'TABLE' || $filter == 'VIEW' || $filter == 'SEQUENCE'$sql .= " AND {$case_clause} = '{$filter}'";
        elseif (
$filter != ''$sql .= " AND FALSE";

        
// Columns
        
$sql .= "
            UNION ALL
            SELECT CASE WHEN relkind='r' THEN (CASE WHEN EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = pc.oid AND r.ev_type = '1') THEN 'COLUMNVIEW'::VARCHAR ELSE 'COLUMNTABLE'::VARCHAR END) WHEN relkind='v' THEN 'COLUMNVIEW'::VARCHAR END,
                NULL, NULL, pc.relname, pa.attname FROM pg_class pc,
                pg_attribute pa WHERE pc.oid=pa.attrelid
                AND pa.attname ~* '.*{$term}.*' AND pa.attnum > 0 AND pc.relkind IN ('r', 'v')"
;
        if (!
$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'COLUMNTABLE' || $filter != 'COLUMNVIEW'$sql .= " AND FALSE";

        
// Functions
        
$sql .= "
            UNION ALL
            SELECT 'FUNCTION', pp.oid, NULL, NULL, pp.proname || '(' || oidvectortypes(pp.proargtypes) || ')' FROM pg_proc pp
                WHERE proname ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND pp.oid > '{$this->_lastSystemOID}'::oid";
        if (
$filter != '' && $filter != 'FUNCTION'$sql .= " AND FALSE";

        
// Indexes
        
$sql .= "
            UNION ALL
            SELECT 'INDEX', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
                pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
                AND pi.indexrelid=pc2.oid
                AND pc2.relname ~* '.*{$term}.*' AND NOT pi.indisprimary AND NOT pi.indisunique"
;
        if (!
$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'INDEX'$sql .= " AND FALSE";

        
// Check Constraints
        
$sql .= "
            UNION ALL
            SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pr.rcname FROM pg_class pc,
                pg_relcheck pr WHERE pc.oid=pr.rcrelid
                AND pr.rcname ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'CONSTRAINT'$sql .= " AND FALSE";

        
// Unique and Primary Key Constraints
        
$sql .= "
            UNION ALL
            SELECT 'CONSTRAINTTABLE', NULL, NULL, pc.relname, pc2.relname FROM pg_class pc,
                pg_index pi, pg_class pc2 WHERE pc.oid=pi.indrelid
                AND pi.indexrelid=pc2.oid
                AND pc2.relname ~* '.*{$term}.*' AND (pi.indisprimary OR pi.indisunique)"
;
        if (!
$conf['show_system']) $sql .= " AND pc2.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'CONSTRAINT'$sql .= " AND FALSE";

        
// Triggers
        
$sql .= "
            UNION ALL
            SELECT 'TRIGGER', NULL, NULL, pc.relname, pt.tgname FROM pg_class pc,
                pg_trigger pt WHERE pc.oid=pt.tgrelid
                AND pt.tgname ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND pc.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'TRIGGER'$sql .= " AND FALSE";

        
// Table Rules
        
$sql .= "
            UNION ALL
            SELECT 'RULETABLE', NULL, NULL, c.relname AS tablename, r.rulename
                FROM pg_rewrite r, pg_class c
                WHERE c.relkind='r' AND NOT EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
                AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'RULE'$sql .= " AND FALSE";

        
// View Rules
        
$sql .= "
            UNION ALL
            SELECT 'RULEVIEW', NULL, NULL, c.relname AS tablename, r.rulename
                FROM pg_rewrite r, pg_class c
                WHERE c.relkind='r' AND EXISTS (SELECT 1 FROM pg_rewrite r WHERE r.ev_class = c.oid AND r.ev_type = '1')
                AND r.rulename !~ '^_RET' AND c.oid = r.ev_class AND r.rulename ~* '.*{$term}.*'"
;
        if (!
$conf['show_system']) $sql .= " AND c.relname NOT LIKE 'pg@_%' ESCAPE '@'";
        if (
$filter != '' && $filter != 'RULE'$sql .= " AND FALSE";

        
// Advanced Objects
        
if ($conf['show_advanced']) {
            
// Types
            
$sql .= "
                UNION ALL
                SELECT 'TYPE', pt.oid, NULL, NULL, pt.typname FROM pg_type pt
                    WHERE typname ~* '.*{$term}.*' AND (pt.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_class c WHERE c.oid = pt.typrelid))"
;
            if (!
$conf['show_system']) $sql .= " AND pt.oid > '{$this->_lastSystemOID}'::oid";
            if (
$filter != '' && $filter != 'TYPE'$sql .= " AND FALSE";

            
// Operators
            
$sql .= "
                UNION ALL
                SELECT 'OPERATOR', po.oid, NULL, NULL, po.oprname FROM pg_operator po
                    WHERE oprname ~* '.*{$term}.*'"
;
            if (!
$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
            if (
$filter != '' && $filter != 'OPERATOR'$sql .= " AND FALSE";

            
// Languages
            
$sql .= "
                UNION ALL
                SELECT 'LANGUAGE', pl.oid, NULL, NULL, pl.lanname FROM pg_language pl
                    WHERE lanname ~* '.*{$term}.*'"
;
            if (!
$conf['show_system']) $sql .= " AND pl.lanispl";
            if (
$filter != '' && $filter != 'LANGUAGE'$sql .= " AND FALSE";

            
// Aggregates
            
$sql .= "
                UNION ALL
                SELECT DISTINCT ON (a.aggname) 'AGGREGATE', a.oid, NULL, NULL, a.aggname FROM pg_aggregate a
                    WHERE aggname ~* '.*{$term}.*'"
;
            if (!
$conf['show_system']) $sql .= " AND a.oid > '{$this->_lastSystemOID}'::oid";
            if (
$filter != '' && $filter != 'AGGREGATE'$sql .= " AND FALSE";

            
// Op Classes
            
$sql .= "
                UNION ALL
                SELECT DISTINCT ON (po.opcname) 'OPCLASS', po.oid, NULL, NULL, po.opcname FROM pg_opclass po
                    WHERE po.opcname ~* '.*{$term}.*'"
;
            if (!
$conf['show_system']) $sql .= " AND po.oid > '{$this->_lastSystemOID}'::oid";
            if (
$filter != '' && $filter != 'OPCLASS'$sql .= " AND FALSE";
        }

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

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

    
/**
     * Private helper method to detect a valid $foo$ quote delimiter at
     * the start of the parameter dquote
     * @return True if valid, false otherwise
     */
    
function valid_dolquote($dquote) {
        
// XXX: support multibyte
        
return (preg_match('/^[$][$]/'$dquote) || preg_match('/^[$][_[:alpha:]][_[:alnum:]]*[$]/'$dquote));
    }

    
/**
     * 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)
     */
    
function advance_1(&$i, &$prevlen, &$thislen) {
        
$prevlen $thislen;
        
$i += $thislen;
        
$thislen 1;
    }

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

    
// Capabilities
    
function hasAlterDatabaseOwner() { return false; }
    function 
hasAlterDatabaseRename() { return false; }
    function 
hasAlterDatabase() { return $this->hasAlterDatabaseRename(); }
    function 
hasAlterSchema() { return false; }
    function 
hasSchemas() { return false; }
    function 
hasConversions() { return false; }
    function 
hasGrantOption() { return false; }
    function 
hasIsClustered() { return false; }
    function 
hasDropBehavior() { return false; }
    function 
hasDropColumn() { return false; }
    function 
hasDomains() { return false; }
    function 
hasAlterDomains() { return false; }
    function 
hasDomainConstraints() { return false; }
    function 
hasAlterTrigger() { return false; }
    function 
hasWithoutOIDs() { return false; }
    function 
hasAlterTableOwner() { return false; }
    function 
hasAlterTableSchema() { return false; }
    function 
hasAlterSequenceOwner() { return false; }
    function 
hasAlterSequenceProps() { return false; }
    function 
hasSequenceAlterSchema() { return false; }
    function 
hasPartialIndexes() { return false; }
    function 
hasCasts() { return false; }
    function 
hasFullSubqueries() { return false; }
    function 
hasPrepare() { return false; }
    function 
hasProcesses() { return false; }
    function 
hasVariables() { return false; }
    function 
hasStatsCollector() { return false; }
    function 
hasAlterColumnType() { return false; }
    function 
hasUserSessionDefaults() { return false; }
    function 
hasUserRename() { return false; }
    function 
hasRecluster() { return false; }
    function 
hasFullVacuum() { return false; }
    function 
hasConstraintsInfo() { return false; }
    function 
hasForeignKeysInfo() { return $this->hasConstraintsInfo(); }
    function 
hasViewColumnRename() { return false; }
    function 
hasTablespaces() { return false; }
    function 
hasSignals() { return false; }
    function 
hasNamedParams() { return false; }
    function 
hasUserAndDbVariables() { return false; }
    function 
hasCompositeTypes() { return false; }
    function 
hasEnumTypes() {return false;}
    function 
hasReadOnlyQueries() { return false; }
    function 
hasFuncPrivs() { return false; }
    function 
hasServerAdminFuncs() { return false; }
    function 
hasRoles() { return false; }
    function 
hasAutovacuum() { return false; }
    function 
hasLocksView() { return false; }
    function 
hasPreparedXacts() { return false; }
    function 
hasDisableTriggers() { return false; }
    function 
hasAlterAggregate() { return false; }
    function 
hasAggregateSortOp() { return false; }
    function 
hasSharedComments() {return false;}
    function 
hasAnalyze() {return false;}
    function 
hasCreateTableLike() {return false;}
    function 
hasCreateTableLikeWithConstraints() {return false;}
    function 
hasCreateTableLikeWithIndexes() {return false;}
    function 
hasFTS() {return false;}
    function 
hasVirtualTransactionId() {return false;}
    function 
hasFunctionCosting() {return false;}
    function 
hasFunctionGUC() {return false;}
    function 
hasFunctionAlterSchema() { return false; }
    function 
hasFunctionAlterOwner() { return false; }
}

?>

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ Read-Only ]

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

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

--[ c99shell v.2.1 [PHP 7 Update] [1.12.2019] maintained by KaizenLouie and updated by cermmik | C99Shell Github (MySQL update) | Generation time: 0.0249 ]--