-<?php\r
-\r
-/**\r
- * A class that implements the DB interface for Postgres\r
- * Note: This class uses ADODB and returns RecordSets.\r
- *\r
- * $Id: Postgres.php,v 1.24 2002/11/18 05:49:55 chriskl Exp $\r
- */\r
-\r
-// @@@ THOUGHT: What about inherits? ie. use of ONLY???\r
-\r
-include_once('../classes/database/BaseDB.php');\r
-\r
-class Postgres extends BaseDB {\r
-\r
- var $dbFields = array('dbname' => 'datname', 'dbcomment' => 'description');\r
- var $tbFields = array('tbname' => 'tablename', 'tbowner' => 'tableowner');\r
- var $vwFields = array('vwname' => 'viewname', 'vwowner' => 'viewowner', 'vwdef' => 'definition');\r
- var $uFields = array('uname' => 'usename', 'usuper' => 'usesuper', 'ucreatedb' => 'usecreatedb', 'uexpires' => 'valuntil');\r
- var $sqFields = array('seqname' => 'relname', 'seqowner' => 'usename', 'lastvalue' => 'last_value', 'incrementby' => 'increment_by', 'maxvalue' => 'max_value', 'minvalue'=> 'min_value', 'cachevalue' => 'cache_value', 'logcount' => 'log_cnt', 'iscycled' => 'is_cycled', 'iscalled' => 'is_called' );\r
- var $ixFields = array('idxname' => 'relname', 'idxdef' => 'pg_get_indexdef', 'uniquekey' => 'indisunique', 'primarykey' => 'indisprimary');\r
- var $tgFields = array('tgname' => 'tgname');\r
-\r
- // Last oid assigned to a system object\r
- var $_lastSystemOID = 18539;\r
- var $_maxNameLen = 31;\r
- \r
- // Name of id column\r
- var $id = 'oid';\r
-\r
- function Postgres($host, $port, $database, $user, $password) {\r
- $this->BaseDB('postgres7');\r
-\r
- //$this->conn->host = $host\r
- //$this->Port = $port;\r
- $pghost = "$host:$port";\r
-\r
- $this->conn->connect($pghost, $user, $password, $database);\r
- }\r
- \r
- /**\r
- * A function to check that the database functions are installed\r
- * and running.\r
- * @return True on success, false otherwise\r
- */\r
- function isLoaded() {\r
- return function_exists('pg_connect');\r
- }\r
- \r
- // Table functions\r
- \r
- /**\r
- * Get the fields for uniquely identifying a row in a table\r
- * @param $table The table for which to retrieve the identifier\r
- * @return An array mapping attribute number to attribute name, empty for no identifiers\r
- * @return -1 error\r
- */\r
- function getRowIdentifier($table) {\r
- $this->clean($table);\r
- \r
- $status = $this->beginTransaction();\r
- if ($status != 0) return -1;\r
- \r
- $sql = "SELECT indrelid, indkey FROM pg_index WHERE indisprimary AND indrelid=(SELECT oid FROM pg_class WHERE relname='{$table}')";\r
- $rs = $this->selectSet($sql);\r
- \r
- // If none, search for OID column\r
- if ($rs->recordCount() == 0) {\r
- $sql = "SELECT relhasoids FROM pg_class WHERE relname='{$table}'";\r
- $rs2 = $this->selectSet($sql);\r
- if ($rs2->recordCount() == 0) {\r
- $this->rollbackTransaction();\r
- return -1;\r
- }\r
- elseif ($rs2->f['relhasoids'] == 'f') {\r
- $this->endTransaction();\r
- return array();\r
- }\r
- else {\r
- $this->endTransaction();\r
- return array(-2 => 'oid');\r
- }\r
- }\r
- // Otherwise select to find the names of the keys\r
- else { \r
- $in = str_replace(' ', ',', $rs->f['indkey']);\r
- $sql = "SELECT attnum, attname FROM pg_attribute WHERE attrelid='{$rs->f['indrelid']}' AND attnum IN ({$in})";\r
- $rs2 = $this->selectSet($sql);\r
- if ($rs2->recordCount() == 0) {\r
- $this->rollbackTransaction();\r
- return -1;\r
- }\r
- else {\r
- $temp = array();\r
- while (!$rs2->EOF) {\r
- $temp[$rs2->f['attnum']] = $rs2->f['attname'];\r
- $rs2->moveNext();\r
- }\r
- $this->endTransaction();\r
- return $temp;\r
- } \r
- } \r
- }\r
- \r
- /**\r
- * Outputs the HTML code for a particular field\r
- * @param $name The name to give the field\r
- * @param $value The value of the field. Note this could be 'numeric(7,2)' sort of thing...\r
- * @param $type The database type of the field\r
- */\r
- function printField($name, $value, $type) {\r
- switch ($type) {\r
- case 'bool':\r
- case 'boolean':\r
- echo "<select name=\"", htmlspecialchars($name), "\">\n";\r
- echo "<option value=\"Y\"", ($value) ? ' selected' : '', ">Yes</option>\n";\r
- echo "<option value=\"N\"", (!$value) ? ' selected' : '', ">No</option>\n";\r
- echo "</select>\n";\r
- break;\r
- case 'text':\r
- case 'bytea':\r
- echo "<textarea name=\"", htmlspecialchars($name), "\" rows=5 cols=28 wrap=virtual style=\"width: 100%\">\n";\r
- echo htmlspecialchars($value);\r
- echo "</textarea>\n";\r
- break;\r
- default:\r
- echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=35>\n";\r
- break;\r
- } \r
- } \r
-\r
- /**\r
- * Return all database available on the server\r
- * @return A list of databases, sorted alphabetically\r
- */\r
- function &getLanguages() {\r
- $sql = "";\r
- return $this->selectSet($sql);\r
- }\r
-\r
- /**\r
- * Return all information about a particular database\r
- * @param $database The name of the database to retrieve\r
- * @return The database info\r
- */\r
- function &getLanguage($database) {\r
- $this->clean($database);\r
- $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";\r
- return $this->selectRow($sql);\r
- }\r
-\r
- /**\r
- * Creates a database\r
- * @param $database The name of the database to create\r
- * @return 0 success\r
- */\r
- function createDatabase($database) {\r
- $this->clean($database);\r
- $sql = "CREATE DATABASE \"{$database}\"";\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Drops a database\r
- * @param $database The name of the database to drop\r
- * @return 0 success\r
- */\r
- function dropDatabase($database) {\r
- $this->clean($database);\r
- $sql = "DROP DATABASE \"{$database}\"";\r
- return $this->execute($sql);\r
- }\r
-\r
- // Table functions\r
-\r
- /**\r
- * Return all tables in current database\r
- * @return All tables, sorted alphabetically \r
- */\r
- function &getTables() {\r
- if (!$this->_showSystem) $where = "WHERE tablename NOT LIKE 'pg_%' ";\r
- else $where = '';\r
- $sql = "SELECT tablename, tableowner FROM pg_tables {$where}ORDER BY tablename";\r
- return $this->selectSet($sql);\r
- }\r
-\r
- /**\r
- * Return all information relating to a table\r
- * @param $table The name of the table\r
- * @return Table information\r
- */\r
- function &getTableByName($table) {\r
- $this->clean($table);\r
- $sql = "SELECT * FROM pg_class WHERE relname='{$table}'";\r
- return $this->selectRow($sql);\r
- }\r
-\r
- /**\r
- * Retrieve the attribute definition of a table\r
- * @param $table The name of the table\r
- * @param $field (optional) The name of a field to return\r
- * @return All attributes in order\r
- */\r
- function &getTableAttributes($table, $field = '') {\r
- $this->clean($table);\r
- $this->clean($field);\r
- \r
- if ($field == '') {\r
- $sql = "SELECT\r
- a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum\r
- FROM\r
- pg_class c, pg_attribute a, pg_type t\r
- WHERE\r
- c.relname = '{$table}' AND a.attnum > 0 AND a.attrelid = c.oid AND a.atttypid = t.oid\r
- ORDER BY a.attnum";\r
- }\r
- else {\r
- $sql = "SELECT\r
- a.attname, t.typname as type, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum\r
- FROM\r
- pg_class c, pg_attribute a, pg_type t\r
- WHERE\r
- c.relname = '{$table}' AND a.attname='{$column}' AND a.attrelid = c.oid AND a.atttypid = t.oid\r
- ORDER BY a.attnum"; \r
- }\r
- \r
- return $this->selectSet($sql);\r
- }\r
-\r
- /**\r
- * Drops a column from a table\r
- * @param $table The table from which to drop a column\r
- * @param $column The column to be dropped\r
- * @param $behavior CASCADE or RESTRICT or empty\r
- * @return 0 success\r
- * @return -99 not implemented\r
- */\r
- function dropColumn($table, $column, $behavior) {\r
- return -99;\r
- }\r
- \r
- /**\r
- * Alters a column in a table\r
- * @param $table The table in which the column resides\r
- * @param $column The column to alter\r
- * @param $name The new name for the column\r
- * @param $notnull (boolean) True if not null, false otherwise\r
- * @param $default The new default for the column\r
- * @return 0 success\r
- * @return -1 set not null error\r
- * @return -2 set default error\r
- * @return -3 rename column error\r
- */\r
- function alterColumn($table, $column, $name, $notnull, $default) {\r
- $this->beginTransaction();\r
-\r
- // @@ NEED TO HANDLE "NESTED" TRANSACTION HERE\r
- $status = $this->setColumnNull($table, $column, !$notnull);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -1;\r
- }\r
-\r
- $status = $this->setColumnDefault($table, $column, $default);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -2;\r
- }\r
-\r
- $status = $this->renameColumn($table, $column, $name);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -3;\r
- }\r
-\r
- return $this->endTransaction();\r
- } \r
-\r
- /**\r
- * Creates a new table in the database\r
- * @param $name The name of the table\r
- * @param $fields The number of fields\r
- * @param $field An array of field names\r
- * @param $type An array of field types\r
- * @param $length An array of field lengths\r
- * @param $notnull An array of not null\r
- * @param $default An array of default values\r
- * @return 0 success\r
- * @return -1 no fields supplied\r
- */\r
- function createTable($name, $fields, $field, $type, $length, $notnull, $default) {\r
- // @@ NOTE: $default field not being cleaned - how on earth DO we clean it??\r
- $this->fieldClean($name);\r
- \r
- $found = false;\r
- $sql = "CREATE TABLE \"{$name}\" (";\r
- \r
- for ($i = 0; $i < $fields; $i++) {\r
- $this->fieldClean($field[$i]);\r
- $this->clean($type[$i]);\r
- $this->clean($length[$i]);\r
- \r
- // Skip blank columns - for user convenience\r
- if ($field[$i] == '' || $type[$i] == '') continue;\r
- \r
- $sql .= "\"{$field[$i]}\" {$type[$i]}";\r
- if ($length[$i] != '') $sql .= "({$length[$i]})";\r
- if (isset($notnull[$i])) $sql .= " NOT NULL";\r
- if ($default[$i] != '') $sql .= " DEFAULT {$default[$i]}";\r
- if ($i != $fields - 1) $sql .= ", ";\r
-\r
- $found = true;\r
- }\r
- \r
- if (!$found) return -1;\r
- \r
- $sql .= ")";\r
- \r
- return $this->execute($sql);\r
- } \r
- \r
- /**\r
- * Removes a table from the database\r
- * @param $table The table to drop\r
- * @return 0 success\r
- */\r
- function dropTable($table) {\r
- $this->fieldClean($table);\r
-\r
- $sql = "DROP TABLE \"{$table}\"";\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Empties a table in the database\r
- * @param $table The table to be emptied\r
- * @return 0 success\r
- */\r
- function emptyTable($table) {\r
- $this->fieldClean($table);\r
-\r
- $sql = "DELETE FROM \"{$table}\"";\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Renames a table\r
- * @param $table The table to be renamed\r
- * @param $newName The new name for the table\r
- * @return 0 success\r
- */\r
- function renameTable($table, $newName) {\r
- $this->fieldClean($table);\r
- $this->fieldClean($newName);\r
- \r
- $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\"";\r
-\r
- // @@ How do you do this?\r
- return $this->execute($sql);\r
- }\r
- \r
- /**\r
- * Returns a recordset of all columns in a table\r
- * @param $table The name of a table\r
- * @param $offset The offset into the table\r
- * @param $limit The maximum number of records to return at once\r
- * @return A recordset\r
- */\r
- function &getTableRows($table) {\r
- $this->fieldClean($table); \r
-\r
- return $this->selectTable("SELECT COUNT(*) FROM \"{$table}\"", $offset, $limit);\r
- } \r
- \r
- /**\r
- * Returns a recordset of all columns in a table\r
- * @param $table The name of a table\r
- * @param $offset The offset into the table\r
- * @param $limit The maximum number of records to return at once\r
- * @return A recordset\r
- */\r
- function &browseTable($table, $offset = null, $limit = null) {\r
- $this->fieldClean($table); \r
- \r
- return $this->selectTable("SELECT oid, * FROM \"{$table}\"", $offset, $limit);\r
- }\r
-\r
- /**\r
- * Returns a recordset of all columns in a table\r
- * @param $table The name of a table\r
- * @param $key The associative array holding the key to retrieve\r
- * @return A recordset\r
- */\r
- function &browseRow($table, $key) {\r
- $this->fieldClean($table);\r
- \r
- $sql = "SELECT * FROM \"{$table}\" WHERE true";\r
- foreach ($key as $k => $v) {\r
- $this->fieldClean($k);\r
- $this->clean($v);\r
- $sql .= " AND \"{$k}\"='{$v}'";\r
- }\r
-\r
- return $this->selectSet($sql);\r
- }\r
-\r
- /**\r
- *\r
- */\r
- function &selectTable($sql, $offset, $limit) {\r
- return $this->selectSet($sql, $offset, $limit);\r
- }\r
-\r
- // Sequence functions\r
- \r
- /**\r
- * Returns all sequences in the current database\r
- * @return A recordset\r
- */\r
- function &getSequences() {\r
- if (!$this->_showSystem) $where = " AND relname NOT LIKE 'pg_%'";\r
- else $where = '';\r
- $sql = "SELECT c.relname, u.usename FROM pg_class c, pg_user u WHERE c.relowner=u.usesysid AND c.relkind = 'S'{$where} ORDER BY relname";\r
- return $this->selectSet( $sql );\r
- }\r
-\r
- /**\r
- * Returns properties of a single sequence\r
- * @return A recordset\r
- */\r
- function &getSequence($sequence) {\r
- if (!$this->_showSystem) $where = " AND relname NOT LIKE 'pg_%'";\r
- else $where = '';\r
- $sql = "SELECT sequence_name as relname,* FROM $sequence"; \r
- return $this->selectSet( $sql );\r
- }\r
-\r
- /** \r
- * Drops a given sequence\r
- * @return 0 success\r
- */\r
- function &dropSequence($sequence) {\r
- $this->clean($sequence);\r
- $sql = "DROP SEQUENCE {$sequence} ";\r
- return $this->execute($sql);\r
- }\r
-\r
- /** \r
- * Creates a new sequence\r
- * @return 0 success\r
- */\r
- function &setSequence($sequence,$startval=1) {\r
- $this->clean($sequence);\r
- $sql = "CREATE SEQUENCE $seq_name START $startval";\r
- return $this->execute($sql);\r
- }\r
-\r
- /** \r
- * Modifies permissions on a given sequence\r
- * @return 0 success\r
- */\r
- function &setSequencePermissions($sequence) {\r
-\r
- }\r
-\r
- /** \r
- * Resets a given sequence to 1\r
- * @return 0 success\r
- */\r
- function &resetSequence($sequence) {\r
- $this->clean($sequence);\r
- $sql = "SELECT setval('$sequence',1)";\r
- return $this->execute($sql);\r
- }\r
-\r
-\r
-\r
-\r
- /**\r
- * Adds a check constraint to a table\r
- * @param $table The table to which to add the check\r
- * @param $definition The definition of the check\r
- * @param $name (optional) The name to give the check, otherwise default name is assigned\r
- * @return 0 success\r
- */\r
- function addCheckConstraint($table, $definition, $name = '') {\r
- $this->fieldClean($table);\r
- $this->fieldClean($name);\r
- // @@ how the heck do you clean definition???\r
- \r
- if ($name != '')\r
- $sql = "ALTER TABLE \"{$table}\" ADD CONSTRAINT \"{$name}\" CHECK ({$definition})";\r
- else\r
- $sql = "ALTER TABLE \"{$table}\" ADD CHECK ({$definition})";\r
-\r
- // @@ How do you do this?\r
- return $this->execute($sql);\r
- }\r
- \r
- /**\r
- * Drops a check constraint from a table\r
- * @param $table The table from which to drop the check\r
- * @param $name The name of the check to be dropped\r
- * @return 0 success\r
- * @return -2 transaction error\r
- * @return -3 lock error\r
- * @return -4 check drop error\r
- */\r
- function dropCheckConstraint($table, $name) {\r
- $this->clean($table);\r
- $this->clean($name);\r
- \r
- // Begin transaction\r
- $status = $this->beginTransaction();\r
- if ($status != 0) return -2;\r
-\r
- // Properly lock the table\r
- $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";\r
- $status = $this->execute($sql);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -3;\r
- }\r
-\r
- // Delete the check constraint\r
- $sql = "DELETE FROM pg_relcheck WHERE rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}') AND rcname='{$name}'";\r
- $status = $this->execute($sql);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -4;\r
- }\r
- \r
- // Update the pg_class catalog to reflect the new number of checks\r
- $sql = "UPDATE pg_class SET relchecks=(SELECT COUNT(*) FROM pg_relcheck WHERE \r
- rcrelid=(SELECT oid FROM pg_class WHERE relname='{$table}')) \r
- WHERE relname='{$table}'";\r
- $status = $this->execute($sql);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -4;\r
- }\r
-\r
- // Otherwise, close the transaction\r
- return $this->endTransaction();\r
- } \r
-\r
- /**\r
- * Adds a unique constraint to a table\r
- * @param $table The table to which to add the unique\r
- * @param $fields (array) An array of fields over which to add the unique\r
- * @param $name (optional) The name to give the unique, otherwise default name is assigned\r
- * @return 0 success\r
- */\r
- function addUniqueConstraint($table, $fields, $name = '') {\r
- $this->fieldClean($table);\r
- $this->arrayClean($fields);\r
- $this->fieldClean($name);\r
- \r
- if ($name != '')\r
- $sql = "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" . join('","', $fields) . "\")";\r
- else return -99; // Not supported\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Drops a unique constraint from a table\r
- * @param $table The table from which to drop the unique\r
- * @param $name The name of the unique\r
- * @return 0 success\r
- */\r
- function dropUniqueConstraint($table, $name) {\r
- $this->fieldClean($name);\r
- \r
- $sql = "DROP INDEX \"{$name}\"";\r
-\r
- return $this->execute($sql);\r
- } \r
- \r
- /**\r
- * Adds a primary key constraint to a table\r
- * @param $table The table to which to add the primery key\r
- * @param $fields (array) An array of fields over which to add the primary key\r
- * @param $name (optional) The name to give the key, otherwise default name is assigned\r
- * @return 0 success\r
- */\r
- function addPrimaryKeyConstraint($table, $fields, $name = '') {\r
- // This function can be faked with a unique index and a catalog twiddle, however\r
- // how do we ensure that it's only used on NOT NULL fields?\r
- return -99; // Not supported.\r
- }\r
-\r
- /**\r
- * Drops a primary key constraint from a table\r
- * @param $table The table from which to drop the primary key\r
- * @param $name The name of the primary key\r
- * @return 0 success\r
- */\r
- function dropPrimaryKeyConstraint($table, $name) {\r
- $this->fieldClean($name);\r
- \r
- $sql = "DROP INDEX \"{$name}\"";\r
-\r
- return $this->execute($sql);\r
- } \r
- \r
- /**\r
- * Changes the owner of a table\r
- * @param $table The table whose owner is to change\r
- * @param $owner The new owner (username) of the table\r
- * @return 0 success\r
- */\r
- function setOwnerOfTable($table, $owner) {\r
- $this->fieldClean($table);\r
- $this->fieldClean($owner);\r
- \r
- $sql = "ALTER TABLE \"{$table}\" OWNER TO \"{$owner}\"";\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- // Column Functions\r
-\r
- /**\r
- * Add a new column to a table\r
- * @param $table The table to add to\r
- * @param $column The name of the new column\r
- * @param $type The type of the column\r
- * @param $size (optional) The optional size of the column (ie. 30 for varchar(30))\r
- * @return 0 success\r
- */\r
- function addColumnToTable($table, $column, $type, $size = '') {\r
- $this->clean($table);\r
- $this->clean($column);\r
- $this->clean($type);\r
- $this->clean($size);\r
- // @@ How the heck do you properly clean type and size?\r
- \r
- if ($size == '')\r
- $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}";\r
- else\r
- $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}({$size})";\r
-\r
- // @@ How do you do this?\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Drops a column from a table\r
- * @param $table The table from which to drop\r
- * @param $column The column name to drop\r
- * @return 0 success\r
- */\r
- function dropColumnFromTable($table, $column) {\r
- return -99; // Not implemented\r
- }\r
-\r
- /**\r
- * Sets default value of a column\r
- * @param $table The table from which to drop\r
- * @param $column The column name to set\r
- * @param $default The new default value\r
- * @return 0 success\r
- */\r
- function setColumnDefault($table, $column, $default) {\r
- $this->fieldClean($table);\r
- $this->fieldClean($column);\r
- // @@ How the heck do you clean default clause?\r
- \r
- $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" SET DEFAULT {$default}";\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Drops default value of a column\r
- * @param $table The table from which to drop\r
- * @param $column The column name to drop default\r
- * @return 0 success\r
- */\r
- function dropColumnDefault($table, $column) {\r
- $this->clean($table);\r
- $this->clean($column);\r
-\r
- $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";\r
-\r
- // @@ How do you do this?\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Sets whether or not a column can contain NULLs\r
- * @param $table The table that contains the column\r
- * @param $column The column to alter\r
- * @param $state True to set null, false to set not null\r
- * @return 0 success\r
- * @return -1 attempt to set not null, but column contains nulls\r
- * @return -2 transaction error\r
- * @return -3 lock error\r
- * @return -4 update error\r
- */\r
- function setColumnNull($table, $column, $state) {\r
- $this->clean($table);\r
- $this->clean($column);\r
-\r
- // Begin transaction\r
- $status = $this->beginTransaction();\r
- if ($status != 0) return -2;\r
-\r
- // Properly lock the table\r
- $sql = "LOCK TABLE \"{$table}\" IN ACCESS EXCLUSIVE MODE";\r
- $status = $this->execute($sql);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -3;\r
- }\r
-\r
- // Check for existing nulls\r
- if (!$state) {\r
- $sql = "SELECT COUNT(*) AS total FROM \"{$table}\" WHERE \"{$column}\" IS NULL";\r
- $result = $this->selectField($sql, 'total');\r
- if ($result > 0) {\r
- $this->rollbackTransaction();\r
- return -1;\r
- }\r
- }\r
-\r
- // Otherwise update the table. Note the reverse-sensed $state variable\r
- $sql = "UPDATE pg_attribute SET attnotnull = " . (($state) ? 'false' : 'true') . " \r
- WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = '{$table}') \r
- AND attname = '{$column}'";\r
-\r
- $status = $this->execute($sql);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -4;\r
- }\r
-\r
- // Otherwise, close the transaction\r
- return $this->endTransaction();\r
- }\r
-\r
- /**\r
- * Renames a column in a table\r
- * @param $table The table containing the column to be renamed\r
- * @param $column The column to be renamed\r
- * @param $newName The new name for the column\r
- * @return 0 success\r
- */\r
- function renameColumn($table, $column, $newName) {\r
- $this->fieldClean($table);\r
- $this->fieldClean($column);\r
- $this->fieldClean($newName);\r
-\r
- $sql = "ALTER TABLE \"{$table}\" RENAME COLUMN \"{$column}\" TO \"{$newName}\"";\r
-\r
- return $this->execute($sql);\r
- }\r
-\r
- /**\r
- * Grabs a list of indicies in the database or table\r
- * @param $table (optional) The name of a table to get the indicies for\r
- */\r
- function &getIndicies($table = '') {\r
- $this->clean($table);\r
-\r
- $sql = "SELECT c2.relname, i.indisprimary, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid)\r
- FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i\r
- WHERE c.oid = '16977' AND c.oid = i.indrelid AND i.indexrelid = c2.oid\r
- ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname";\r
-\r
- \r
- if ($table != '')\r
- $where = "WHERE relname='{$table}' AND ";\r
- elseif (!$this->_showSystem)\r
- $where = "WHERE relname NOT LIKE 'pg_%' AND ";\r
- else $where = '';\r
- \r
- $sql = "SELECT relname FROM pg_class {$where} relkind ='i' ORDER BY relname";\r
-\r
- return $this->selectSet($sql);\r
- }\r
-\r
- function &getIndex($idxname) {\r
- $sql = "SELECT\r
- ic.relname AS relname,\r
- bc.relname AS tab_name,\r
- ta.attname AS column_name,\r
- i.indisunique AS unique_key,\r
- i.indisprimary AS primary_key\r
- FROM\r
- pg_class bc,\r
- pg_class ic,\r
- pg_index i,\r
- pg_attribute ta,\r
- pg_attribute ia\r
- WHERE\r
- bc.oid = i.indrelid\r
- AND ic.oid = i.indexrelid\r
- AND ia.attrelid = i.indexrelid\r
- AND ta.attrelid = bc.oid\r
- AND ic.relname = '$idxname'\r
- AND ta.attrelid = i.indrelid\r
- AND ta.attnum = i.indkey[ia.attnum-1]\r
- ORDER BY\r
- relname, tab_name, column_name";\r
-\r
- return $this->selectSet($sql);\r
- }\r
-\r
-\r
-/*\r
- function setIndex()\r
- function delIndex()\r
-\r
-\r
- // DML Functions\r
-\r
- function doSelect()\r
- function doDelete()\r
- function doUpdate()\r
-*/\r
-\r
- // View functions\r
- \r
- /**\r
- * Returns a list of all views in the database\r
- * @return All views\r
- */\r
- function getViews() {\r
- if (!$this->_showSystem)\r
- $where = "WHERE viewname NOT LIKE 'pg_%'";\r
- else $where = '';\r
- \r
- $sql = "SELECT viewname, viewowner FROM pg_views {$where} ORDER BY viewname";\r
-\r
- return $this->selectSet($sql);\r
- }\r
- \r
- /**\r
- * Returns all details for a particular view\r
- * @param $view The name of the view to retrieve\r
- * @return View info\r
- */\r
- function getView($view) {\r
- $this->clean($view);\r
- \r
- $sql = "SELECT viewname, viewowner, definition FROM pg_views WHERE viewname='$view'";\r
-\r
- return $this->selectSet($sql);\r
- } \r
-\r
- /**\r
- * Creates a new view.\r
- * @param $viewname The name of the view to create\r
- * @param $definition The definition for the new view\r
- * @return 0 success\r
- */\r
- function createView($viewname, $definition) {\r
- $this->clean($viewname);\r
- // Note: $definition not cleaned\r
- \r
- $sql = "CREATE VIEW \"{$viewname}\" AS {$definition}";\r
- \r
- return $this->execute($sql);\r
- }\r
- \r
- /**\r
- * Drops a view.\r
- * @param $viewname The name of the view to drop\r
- * @return 0 success\r
- */\r
- function dropView($viewname) {\r
- $this->clean($viewname);\r
- \r
- $sql = "DROP VIEW \"{$viewname}\"";\r
- \r
- return $this->execute($sql);\r
- }\r
- \r
- /**\r
- * Updates a view. Postgres doesn't have CREATE OR REPLACE view,\r
- * so we do it with a drop and a recreate.\r
- * @param $viewname The name fo the view to update\r
- * @param $definition The new definition for the view\r
- * @return 0 success\r
- * @return -1 transaction error\r
- * @return -2 drop view error\r
- * @return -3 create view error\r
- */\r
- function setView($viewname, $definition) {\r
- $status = $this->beginTransaction();\r
- if ($status != 0) return -1;\r
- \r
- $status = $this->dropView($viewname);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -2;\r
- }\r
- \r
- $status = $this->createView($viewname, $definition);\r
- if ($status != 0) {\r
- $this->rollbackTransaction();\r
- return -3;\r
- }\r
- \r
- $status = $this->endTransaction();\r
- return ($status == 0) ? 0 : -1;\r
- } \r
-\r
- // Operator functions\r
- \r
- /**\r
- * Returns a list of all operators in the database\r
- * @return All operators\r
- */\r
- function getOperators() {\r
- if (!$this->_showSystem)\r
- $where = "WHERE po.oid > '{$this->_lastSystemOID}'::oid";\r
- else $where = '';\r
- \r
- $sql = "\r
- SELECT\r
- po.oid,\r
- po.oprname,\r
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprleft) AS oprleftname,\r
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprright) AS oprrightname,\r
- (SELECT typname FROM pg_type pt WHERE pt.oid=po.oprresult) AS resultname\r
- FROM\r
- pg_operator po\r
- {$where} \r
- ORDER BY\r
- po.oprname, po.oid\r
- ";\r
-\r
- return $this->selectSet($sql);\r
- }\r
- \r
- \r
- /**\r
- * Creates a new operator\r
- */\r
-\r
- // User and group functions\r
- \r
- /**\r
- * Returns all users in the database cluster\r
- * @return All users\r
- */\r
- function &getUsers() {\r
- $sql = "SELECT usename, usesuper, usecreatedb, valuntil FROM pg_shadow ORDER BY usename";\r
- \r
- return $this->selectSet($sql);\r
- }\r
- \r
- /**\r
- * Return information about a single user\r
- * @param $username The username of the user to retrieve\r
- * @return The user's data\r
- */\r
- function &getUser($username) {\r
- $this->clean($username);\r
- \r
- $sql = "SELECT usename, usesuper, usecreatedb, valuntil FROM pg_shadow WHERE usename='{$username}'";\r
- \r
- return $this->selectSet($sql);\r
- }\r
- \r
- /**\r
- * Creates a new user\r
- * @param $username The username of the user to create\r
- * @param $password A password for the user\r
- * @param $createdb boolean Whether or not the user can create databases\r
- * @param $createuser boolean Whether or not the user can create other users\r
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. When the account expires.\r
- * @param $group (array) The groups to create the user in\r
- * @return 0 success\r
- */\r
- function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {\r
- $this->clean($username);\r
- // @@ THIS IS A PROBLEM FOR TRIMMING PASSWORD!!!\r
- $this->clean($password);\r
- $this->clean($expiry);\r
- $this->arrayClean($groups); \r
- \r
- $sql = "CREATE USER \"{$username}\"";\r
- if ($password != '') $sql .= " WITH PASSWORD '{$password}'";\r
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';\r
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';\r
- if (is_array($groups) && sizeof($groups) > 0) $sql .= " IN GROUP '" . join("', '", $groups) . "'";\r
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";\r
- \r
- return $this->execute($sql);\r
- } \r
- \r
- /**\r
- * Adjusts a user's info\r
- * @param $username The username of the user to modify\r
- * @param $password A new password for the user\r
- * @param $createdb boolean Whether or not the user can create databases\r
- * @param $createuser boolean Whether or not the user can create other users\r
- * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. When the account expires.\r
- * @return 0 success\r
- */\r
- function setUser($username, $password, $createdb, $createuser, $expiry) {\r
- $this->clean($username);\r
- $this->clean($password);\r
- $this->clean($expiry);\r
- \r
- $sql = "ALTER USER \"{$username}\"";\r
- if ($password != '') $sql .= " WITH PASSWORD '{$password}'";\r
- $sql .= ($createdb) ? ' CREATEDB' : ' NOCREATEDB';\r
- $sql .= ($createuser) ? ' CREATEUSER' : ' NOCREATEUSER';\r
- if ($expiry != '') $sql .= " VALID UNTIL '{$expiry}'";\r
- \r
- return $this->execute($sql);\r
- } \r
- \r
- /**\r
- * Removes a user\r
- * @param $username The username of the user to drop\r
- * @return 0 success\r
- */\r
- function dropUser($username) {\r
- $this->clean($username);\r
- \r
- $sql = "DROP USER \"{$username}\"";\r
- \r
- return $this->execute($sql);\r
- }\r
- \r
- // Capabilities\r
- function hasTables() { return true; }\r
- function hasViews() { return true; }\r
- function hasSequences() { return true; }\r
- function hasFunctions() { return true; }\r
- function hasTriggers() { return true; }\r
- function hasOperators() { return true; }\r
- function hasTypes() { return true; }\r
- function hasAggregates() { return true; }\r
- function hasIndicies() { return true; }\r
- function hasRules() { return true; }\r
- function hasLanguages() { return true; }\r
-\r
-}\r
-\r
-?>\r
+<?php
+
+/**
+ * A class that implements the DB interface for Postgres
+ * Note: This class uses ADODB and returns RecordSets.
+ *
+ * $Id: Postgres.php,v 1.25 2002/12/14 10:56:26 chriskl Exp $
+ */
+
+// @@@ THOUGHT: What about inherits? ie. use of ONLY???
+
+include_once('../classes/database/BaseDB.php');
+
+class Postgres extends BaseDB {
+
+ var $dbFields = array('dbname' => 'datname', 'dbcomment' => 'description');
+ var $tbFields = array('tbname' => 'tablename', 'tbowner' => 'tableowner');
+ var $vwFields = array('vwname' => 'viewname', 'vwowner' => 'viewowner', 'vwdef' => 'definition');
+ var $uFields = array('uname' => 'usename', 'usuper' => 'usesuper', 'ucreatedb' => 'usecreatedb', 'uexpires' => 'valuntil');
+ var $sqFields = array('seqname' => 'relname', 'seqowner' => 'usename', 'lastvalue' => 'last_value', 'incrementby' => 'increment_by', 'maxvalue' => 'max_value', 'minvalue'=> 'min_value', 'cachevalue' => 'cache_value', 'logcount' => 'log_cnt', 'iscycled' => 'is_cycled', 'iscalled' => 'is_called' );
+ var $ixFields = array('idxname' => 'relname', 'idxdef' => 'pg_get_indexdef', 'uniquekey' => 'indisunique', 'primarykey' => 'indisprimary');
+ var $tgFields = array('tgname' => 'tgname');
+
+ // Last oid assigned to a system object
+ var $_lastSystemOID = 18539;
+ var $_maxNameLen = 31;
+
+ // Name of id column
+ var $id = 'oid';
+
+ function Postgres($host, $port, $database, $user, $password) {
+ $this->BaseDB('postgres7');
+
+ //$this->conn->host = $host
+ //$this->Port = $port;
+ $pghost = "$host:$port";
+
+ $this->conn->connect($pghost, $user, $password, $database);
+ }
+
+ /**
+ * A function to check that the database functions are installed
+ * and running.
+ * @return True on success, false otherwise
+ */
+ function isLoaded() {
+ return function_exists('pg_connect');
+ }
+
+ // Table functions
+
+ /**
+ * Get the fields for uniquely identifying a row in a table
+ * @param $table The table for which to retrieve the identifier
+ * @return An array mapping attribute number to attribute name, empty for no identifiers
+ * @return -1 error
+ */
+ function getRowIdentifier($table) {
+ $this->clean($table);
+
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $sql = "SELECT indrelid, indkey FROM pg_index WHERE indisprimary AND indrelid=(SELECT oid FROM pg_class WHERE relname='{$table}')";
+ $rs = $this->selectSet($sql);
+
+ // If none, search for OID column
+ if ($rs->recordCount() == 0) {
+ $sql = "SELECT relhasoids FROM pg_class WHERE relname='{$table}'";
+ $rs2 = $this->selectSet($sql);
+ if ($rs2->recordCount() == 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ elseif ($rs2->f['relhasoids'] == 'f') {
+ $this->endTransaction();
+ return array();
+ }
+ else {
+ $this->endTransaction();
+ return array(-2 => 'oid');
+ }
+ }
+ // Otherwise select to find the names of the keys
+ else {
+ $in = str_replace(' ', ',', $rs->f['indkey']);
+ $sql = "SELECT attnum, attname FROM pg_attribute WHERE attrelid='{$rs->f['indrelid']}' AND attnum IN ({$in})";
+ $rs2 = $this->selectSet($sql);
+ if ($rs2->recordCount() == 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+ else {
+ $temp = array();
+ while (!$rs2->EOF) {
+ $temp[$rs2->f['attnum']] = $rs2->f['attname'];
+ $rs2->moveNext();
+ }
+ $this->endTransaction();
+ return $temp;
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ function printField($name, $value, $type) {
+ switch ($type) {
+ case 'bool':
+ case 'boolean':
+ echo "<select name=\"", htmlspecialchars($name), "\">\n";
+ echo "<option value=\"Y\"", ($value) ? ' selected' : '', ">Yes</option>\n";
+ echo "<option value=\"N\"", (!$value) ? ' selected' : '', ">No</option>\n";
+ echo "</select>\n";
+ break;
+ case 'text':
+ case 'bytea':
+ echo "<textarea name=\"", htmlspecialchars($name), "\" rows=5 cols=28 wrap=virtual style=\"width: 100%\">\n";
+ echo htmlspecialchars($value);
+ echo "</textarea>\n";
+ break;
+ default:
+ echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=35>\n";
+ break;
+ }
+ }
+
+ /**
+ * Return all database available on the server
+ * @return A list of databases, sorted alphabetically
+ */
+ function &getLanguages() {
+ $sql = "";
+ 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 &getLanguage($database) {
+ $this->clean($database);
+ $sql = "SELECT * FROM pg_database WHERE datname='{$database}'";
+ return $this->selectRow($sql);
+ }
+
+ /**
+ * Creates a database
+ * @param $database The name of the database to create
+ * @return 0 success
+ */
+ function createDatabase($database) {
+ $this->clean($database);
+ $sql = "CREATE DATABASE \"{$database}\"";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Drops a database
+ * @param $database The name of the database to drop
+ * @return 0 success
+ */
+ function dropDatabase($database) {
+ $this->clean($database);
+ $sql = "DROP DATABASE \"{$database}\"";
+ return $this->execute($sql);
+ }
+
+ // Table functions
+
+ /**
+ * Return all tables in current database
+ * @return All tables, sorted alphabetically
+ */
+ function &getTables() {
+ if (!$this->_showSystem) $where = "WHERE tablename NOT LIKE 'pg_%' ";
+ else $where = '';
+ $sql = "SELECT tablename, tableowner FROM pg_tables {$where}ORDER BY tablename";
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Return all information relating to a table
+ * @param $table The name of the table
+ * @return Table information
+ */
+ function &getTableByName($table) {
+ $this->clean($table);
+ $sql = "SELECT * FROM pg_class WHERE relname='{$table}'";
+ return $this->selectRow($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, a.attnum
+ FROM
+ pg_class c, pg_attribute a, 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, a.attlen, a.atttypmod, a.attnotnull, a.atthasdef, a.attnum
+ FROM
+ pg_class c, pg_attribute a, pg_type t
+ WHERE
+ c.relname = '{$table}' AND a.attname='{$column}' AND a.attrelid = c.oid AND a.atttypid = t.oid
+ ORDER BY a.attnum";
+ }
+
+ return $this->selectSet($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 $behavior CASCADE or RESTRICT or empty
+ * @return 0 success
+ * @return -99 not implemented
+ */
+ function dropColumn($table, $column, $behavior) {
+ return -99;
+ }
+
+ /**
+ * Alters a column in a table
+ * @param $table The table in which the column resides
+ * @param $column The column to alter
+ * @param $name The new name for the column
+ * @param $notnull (boolean) True if not null, false otherwise
+ * @param $default The new default for the column
+ * @return 0 success
+ * @return -1 set not null error
+ * @return -2 set default error
+ * @return -3 rename column error
+ */
+ function alterColumn($table, $column, $name, $notnull, $default) {
+ $this->beginTransaction();
+
+ // @@ NEED TO HANDLE "NESTED" TRANSACTION HERE
+ $status = $this->setColumnNull($table, $column, !$notnull);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -1;
+ }
+
+ $status = $this->setColumnDefault($table, $column, $default);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+
+ $status = $this->renameColumn($table, $column, $name);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+
+ return $this->endTransaction();
+ }
+
+ /**
+ * 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 $length An array of field lengths
+ * @param $notnull An array of not null
+ * @param $default An array of default values
+ * @return 0 success
+ * @return -1 no fields supplied
+ */
+ function createTable($name, $fields, $field, $type, $length, $notnull, $default) {
+ // @@ NOTE: $default field not being cleaned - how on earth DO we clean it??
+ $this->fieldClean($name);
+
+ $found = false;
+ $sql = "CREATE TABLE \"{$name}\" (";
+
+ for ($i = 0; $i < $fields; $i++) {
+ $this->fieldClean($field[$i]);
+ $this->clean($type[$i]);
+ $this->clean($length[$i]);
+
+ // Skip blank columns - for user convenience
+ if ($field[$i] == '' || $type[$i] == '') continue;
+
+ $sql .= "\"{$field[$i]}\" {$type[$i]}";
+ if ($length[$i] != '') $sql .= "({$length[$i]})";
+ if (isset($notnull[$i])) $sql .= " NOT NULL";
+ if ($default[$i] != '') $sql .= " DEFAULT {$default[$i]}";
+ if ($i != $fields - 1) $sql .= ", ";
+
+ $found = true;
+ }
+
+ if (!$found) return -1;
+
+ $sql .= ")";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes a table from the database
+ * @param $table The table to drop
+ * @return 0 success
+ */
+ function dropTable($table) {
+ $this->fieldClean($table);
+
+ $sql = "DROP TABLE \"{$table}\"";
+
+ 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
+ */
+ function renameTable($table, $newName) {
+ $this->fieldClean($table);
+ $this->fieldClean($newName);
+
+ $sql = "ALTER TABLE \"{$table}\" RENAME TO \"{$newName}\"";
+
+ // @@ How do you do this?
+ return $this->execute($sql);
+ }
+
+ /**
+ * Returns a recordset of all columns in a table
+ * @param $table The name of a table
+ * @param $offset The offset into the table
+ * @param $limit The maximum number of records to return at once
+ * @return A recordset
+ */
+ function &getTableRows($table) {
+ $this->fieldClean($table);
+
+ return $this->selectTable("SELECT COUNT(*) FROM \"{$table}\"", $offset, $limit);
+ }
+
+ /**
+ * Returns a recordset of all columns in a table
+ * @param $table The name of a table
+ * @param $offset The offset into the table
+ * @param $limit The maximum number of records to return at once
+ * @return A recordset
+ */
+ function &browseTable($table, $offset = null, $limit = null) {
+ $this->fieldClean($table);
+
+ return $this->selectTable("SELECT oid, * FROM \"{$table}\"", $offset, $limit);
+ }
+
+ /**
+ * 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);
+
+ $sql = "SELECT * FROM \"{$table}\" WHERE true";
+ foreach ($key as $k => $v) {
+ $this->fieldClean($k);
+ $this->clean($v);
+ $sql .= " AND \"{$k}\"='{$v}'";
+ }
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ *
+ */
+ function &selectTable($sql, $offset, $limit) {
+ return $this->selectSet($sql, $offset, $limit);
+ }
+
+ // Sequence functions
+
+ /**
+ * Returns all sequences in the current database
+ * @return A recordset
+ */
+ function &getSequences() {
+ if (!$this->_showSystem) $where = " AND relname NOT LIKE 'pg_%'";
+ else $where = '';
+ $sql = "SELECT c.relname, u.usename FROM pg_class c, pg_user u WHERE c.relowner=u.usesysid AND c.relkind = 'S'{$where} ORDER BY relname";
+ return $this->selectSet( $sql );
+ }
+
+ /**
+ * Returns properties of a single sequence
+ * @return A recordset
+ */
+ function &getSequence($sequence) {
+ if (!$this->_showSystem) $where = " AND relname NOT LIKE 'pg_%'";
+ else $where = '';
+ $sql = "SELECT sequence_name as relname,* FROM $sequence";
+ return $this->selectSet( $sql );
+ }
+
+ /**
+ * Drops a given sequence
+ * @return 0 success
+ */
+ function &dropSequence($sequence) {
+ $this->clean($sequence);
+ $sql = "DROP SEQUENCE {$sequence} ";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Creates a new sequence
+ * @return 0 success
+ */
+ function &setSequence($sequence,$startval=1) {
+ $this->clean($sequence);
+ $sql = "CREATE SEQUENCE $seq_name START $startval";
+ return $this->execute($sql);
+ }
+
+ /**
+ * Modifies permissions on a given sequence
+ * @return 0 success
+ */
+ function &setSequencePermissions($sequence) {
+
+ }
+
+ /**
+ * Resets a given sequence to 1
+ * @return 0 success
+ */
+ function &resetSequence($sequence) {
+ $this->clean($sequence);
+ $sql = "SELECT setval('$sequence',1)";
+ return $this->execute($sql);
+ }
+
+
+
+
+ /**
+ * Adds a check constraint to a table
+ * @param $table The table to which to add the check
+ * @param $definition The definition of the check
+ * @param $name (optional) The name to give the check, otherwise default name is assigned
+ * @return 0 success
+ */
+ function addCheckConstraint($table, $definition, $name = '') {
+ $this->fieldClean($table);
+ $this->fieldClean($name);
+ // @@ how the heck do you clean definition???
+
+ if ($name != '')
+ $sql = "ALTER TABLE \"{$table}\" ADD CONSTRAINT \"{$name}\" CHECK ({$definition})";
+ else
+ $sql = "ALTER TABLE \"{$table}\" ADD CHECK ({$definition})";
+
+ // @@ How do you do this?
+ 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();
+ }
+
+ /**
+ * 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
+ */
+ function addUniqueConstraint($table, $fields, $name = '') {
+ $this->fieldClean($table);
+ $this->arrayClean($fields);
+ $this->fieldClean($name);
+
+ if ($name != '')
+ $sql = "CREATE UNIQUE INDEX \"{$name}\" ON \"{$table}\"(\"" . join('","', $fields) . "\")";
+ else return -99; // Not supported
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Drops a unique constraint from a table
+ * @param $table The table from which to drop the unique
+ * @param $name The name of the unique
+ * @return 0 success
+ */
+ function dropUniqueConstraint($table, $name) {
+ $this->fieldClean($name);
+
+ $sql = "DROP INDEX \"{$name}\"";
+
+ 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 addPrimaryKeyConstraint($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.
+ }
+
+ /**
+ * Drops a primary key constraint from a table
+ * @param $table The table from which to drop the primary key
+ * @param $name The name of the primary key
+ * @return 0 success
+ */
+ function dropPrimaryKeyConstraint($table, $name) {
+ $this->fieldClean($name);
+
+ $sql = "DROP INDEX \"{$name}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Changes the owner of a table
+ * @param $table The table whose owner is to change
+ * @param $owner The new owner (username) of the table
+ * @return 0 success
+ */
+ function setOwnerOfTable($table, $owner) {
+ $this->fieldClean($table);
+ $this->fieldClean($owner);
+
+ $sql = "ALTER TABLE \"{$table}\" OWNER TO \"{$owner}\"";
+
+ return $this->execute($sql);
+ }
+
+ // Column Functions
+
+ /**
+ * 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 $size (optional) The optional size of the column (ie. 30 for varchar(30))
+ * @return 0 success
+ */
+ function addColumnToTable($table, $column, $type, $size = '') {
+ $this->clean($table);
+ $this->clean($column);
+ $this->clean($type);
+ $this->clean($size);
+ // @@ How the heck do you properly clean type and size?
+
+ if ($size == '')
+ $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}";
+ else
+ $sql = "ALTER TABLE \"{$table}\" ADD COLUMN \"{$column}\" {$type}({$size})";
+
+ // @@ How do you do this?
+ return $this->execute($sql);
+ }
+
+ /**
+ * Drops a column from a table
+ * @param $table The table from which to drop
+ * @param $column The column name to drop
+ * @return 0 success
+ */
+ function dropColumnFromTable($table, $column) {
+ return -99; // Not implemented
+ }
+
+ /**
+ * 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);
+ // @@ How the heck do you clean default clause?
+
+ $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->clean($table);
+ $this->clean($column);
+
+ $sql = "ALTER TABLE \"{$table}\" ALTER COLUMN \"{$column}\" DROP DEFAULT";
+
+ // @@ How do you do this?
+ 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->clean($table);
+ $this->clean($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);
+ }
+
+ /**
+ * Grabs a list of indicies in the database or table
+ * @param $table (optional) The name of a table to get the indicies for
+ */
+ function &getIndicies($table = '') {
+ $this->clean($table);
+
+ $sql = "SELECT c2.relname, i.indisprimary, i.indisunique, pg_catalog.pg_get_indexdef(i.indexrelid)
+ FROM pg_catalog.pg_class c, pg_catalog.pg_class c2, pg_catalog.pg_index i
+ WHERE c.oid = '16977' AND c.oid = i.indrelid AND i.indexrelid = c2.oid
+ ORDER BY i.indisprimary DESC, i.indisunique DESC, c2.relname";
+
+
+ if ($table != '')
+ $where = "WHERE relname='{$table}' AND ";
+ elseif (!$this->_showSystem)
+ $where = "WHERE relname NOT LIKE 'pg_%' AND ";
+ else $where = '';
+
+ $sql = "SELECT relname FROM pg_class {$where} relkind ='i' ORDER BY relname";
+
+ return $this->selectSet($sql);
+ }
+
+ function &getIndex($idxname) {
+ $sql = "SELECT
+ ic.relname AS relname,
+ bc.relname AS tab_name,
+ ta.attname AS column_name,
+ i.indisunique AS unique_key,
+ i.indisprimary AS primary_key
+ FROM
+ pg_class bc,
+ pg_class ic,
+ pg_index i,
+ pg_attribute ta,
+ pg_attribute ia
+ WHERE
+ bc.oid = i.indrelid
+ AND ic.oid = i.indexrelid
+ AND ia.attrelid = i.indexrelid
+ AND ta.attrelid = bc.oid
+ AND ic.relname = '$idxname'
+ AND ta.attrelid = i.indrelid
+ AND ta.attnum = i.indkey[ia.attnum-1]
+ ORDER BY
+ relname, tab_name, column_name";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates a database
+ * @param $database The name of the database to create
+ * @return 0 success
+ */
+ function createIndex($name, $table, $columns) {
+ $this->fieldClean($name);
+ $this->fieldClean($table);
+ $this->arrayClean($columns);
+
+ $sql = "CREATE INDEX \"{$name}\" ON \"{$table}\"(\"" .
+ implode('","', $columns) . "\")";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes an index from the database
+ * @param $index The index to drop
+ * @return 0 success
+ */
+ function dropIndex($index) {
+ $this->fieldClean($index);
+
+ $sql = "DROP INDEX \"{$index}\"";
+
+ return $this->execute($sql);
+ }
+
+ // View functions
+
+ /**
+ * Returns a list of all views in the database
+ * @return All views
+ */
+ function getViews() {
+ if (!$this->_showSystem)
+ $where = "WHERE viewname NOT LIKE 'pg_%'";
+ else $where = '';
+
+ $sql = "SELECT viewname, viewowner FROM pg_views {$where} ORDER BY viewname";
+
+ 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, viewowner, definition FROM pg_views 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
+ * @return 0 success
+ */
+ function createView($viewname, $definition) {
+ $this->clean($viewname);
+ // Note: $definition not cleaned
+
+ $sql = "CREATE VIEW \"{$viewname}\" AS {$definition}";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Drops a view.
+ * @param $viewname The name of the view to drop
+ * @return 0 success
+ */
+ function dropView($viewname) {
+ $this->clean($viewname);
+
+ $sql = "DROP VIEW \"{$viewname}\"";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Updates a view. Postgres doesn'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
+ */
+ function setView($viewname, $definition) {
+ $status = $this->beginTransaction();
+ if ($status != 0) return -1;
+
+ $status = $this->dropView($viewname);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -2;
+ }
+
+ $status = $this->createView($viewname, $definition);
+ if ($status != 0) {
+ $this->rollbackTransaction();
+ return -3;
+ }
+
+ $status = $this->endTransaction();
+ return ($status == 0) ? 0 : -1;
+ }
+
+ // Operator functions
+
+ /**
+ * Returns a list of all operators in the database
+ * @return All operators
+ */
+ function getOperators() {
+ if (!$this->_showSystem)
+ $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
+ FROM
+ pg_operator po
+ {$where}
+ ORDER BY
+ po.oprname, po.oid
+ ";
+
+ return $this->selectSet($sql);
+ }
+
+
+ /**
+ * Creates a new operator
+ */
+
+ // User and group functions
+
+ /**
+ * Returns all users in the database cluster
+ * @return All users
+ */
+ function &getUsers() {
+ $sql = "SELECT usename, usesuper, usecreatedb, valuntil FROM pg_shadow ORDER BY usename";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Return 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 FROM pg_shadow WHERE usename='{$username}'";
+
+ return $this->selectSet($sql);
+ }
+
+ /**
+ * Creates a new user
+ * @param $username The username of the user to create
+ * @param $password A password for the user
+ * @param $createdb boolean Whether or not the user can create databases
+ * @param $createuser boolean Whether or not the user can create other users
+ * @param $expiry string Format 'YYYY-MM-DD HH:MM:SS'. When the account expires.
+ * @param $group (array) The groups to create the user in
+ * @return 0 success
+ */
+ function createUser($username, $password, $createdb, $createuser, $expiry, $groups) {
+ $this->clean($username);
+ // @@ THIS IS A PROBLEM FOR TRIMMING PASSWORD!!!
+ $this->clean($password);
+ $this->clean($expiry);
+ $this->arrayClean($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}'";
+
+ 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'. When the account expires.
+ * @return 0 success
+ */
+ function setUser($username, $password, $createdb, $createuser, $expiry) {
+ $this->clean($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}'";
+
+ return $this->execute($sql);
+ }
+
+ /**
+ * Removes a user
+ * @param $username The username of the user to drop
+ * @return 0 success
+ */
+ function dropUser($username) {
+ $this->clean($username);
+
+ $sql = "DROP USER \"{$username}\"";
+
+ return $this->execute($sql);
+ }
+
+ // Capabilities
+ function hasTables() { return true; }
+ function hasViews() { return true; }
+ function hasSequences() { return true; }
+ function hasFunctions() { return true; }
+ function hasTriggers() { return true; }
+ function hasOperators() { return true; }
+ function hasTypes() { return true; }
+ function hasAggregates() { return true; }
+ function hasIndicies() { return true; }
+ function hasRules() { return true; }
+ function hasLanguages() { return true; }
+
+}
+
+?>