2008-11-23 02:20:39 +01:00
< ? php
/**
* @ package sapphire
* @ subpackage model
*/
/**
* PostgreSQL connector class .
* @ package sapphire
* @ subpackage model
*/
class PostgreSQLDatabase extends Database {
/**
* Connection to the DBMS .
* @ var resource
*/
private $dbConn ;
/**
* True if we are connected to a database .
* @ var boolean
*/
private $active ;
/**
* The name of the database .
* @ var string
*/
private $database ;
/**
* Connect to a PostgreSQL database .
* @ param array $parameters An map of parameters , which should include :
* - server : The server , eg , localhost
* - username : The username to log on with
* - password : The password to log on with
* - database : The database to connect to
*/
public function __construct ( $parameters ) {
( $parameters [ 'username' ] != '' ) ? $username = ' user=' . $parameters [ 'username' ] : $username = '' ;
( $parameters [ 'password' ] != '' ) ? $password = ' password=' . $parameters [ 'password' ] : $password = '' ;
//assumes that the server and dbname will always be provided:
$this -> dbConn = pg_connect ( 'host=' . $parameters [ 'server' ] . ' port=5432 dbname=' . $parameters [ 'database' ] . $username . $password );
//By virtue of getting here, the connection is active:
$this -> active = true ;
$this -> database = $parameters [ 'database' ];
if ( ! $this -> dbConn ) {
$this -> databaseError ( " Couldn't connect to PostgreSQL database " );
}
parent :: __construct ();
}
/**
* Not implemented , needed for PDO
*/
public function getConnect ( $parameters ) {
return null ;
}
/**
* Returns true if this database supports collations
* @ return boolean
*/
public function supportsCollations () {
return $this -> getVersion () >= 4.1 ;
}
/**
* The version of MySQL .
* @ var float
*/
private $pgsqlVersion ;
/**
* Get the version of MySQL .
* @ return float
*/
public function getVersion () {
if ( ! $this -> pgsqlVersion ) {
//returns something like this: PostgreSQL 8.3.3 on i386-apple-darwin9.3.0, compiled by GCC i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5465)
$postgres = strlen ( 'PostgreSQL ' );
$db_version = $this -> query ( " SELECT VERSION() " ) -> value ();
$this -> pgsqlVersion = ( float ) trim ( substr ( $db_version , $postgres , strpos ( $db_version , ' on ' )));
}
return $this -> pgsqlVersion ;
}
/**
* Get the database server , namely mysql .
* @ return string
*/
public function getDatabaseServer () {
return " postgresql " ;
}
public function query ( $sql , $errorLevel = E_USER_ERROR ) {
if ( isset ( $_REQUEST [ 'previewwrite' ]) && in_array ( strtolower ( substr ( $sql , 0 , strpos ( $sql , ' ' ))), array ( 'insert' , 'update' , 'delete' , 'replace' ))) {
Debug :: message ( " Will execute: $sql " );
return ;
}
if ( isset ( $_REQUEST [ 'showqueries' ])) {
$starttime = microtime ( true );
}
2009-01-09 04:43:28 +01:00
2008-11-23 02:20:39 +01:00
echo 'sql: ' . $sql . '<br>' ;
2009-01-09 04:43:28 +01:00
2008-11-23 02:20:39 +01:00
$handle = pg_query ( $this -> dbConn , $sql );
if ( isset ( $_REQUEST [ 'showqueries' ])) {
$endtime = round ( microtime ( true ) - $starttime , 4 );
Debug :: message ( " \n $sql\n { $endtime } ms \n " , false );
}
DB :: $lastQuery = $handle ;
if ( ! $handle && $errorLevel ) $this -> databaseError ( " Couldn't run query: $sql | " . pgsql_error ( $this -> dbConn ), $errorLevel );
return new PostgreSQLQuery ( $this , $handle );
}
public function getGeneratedID ( $table ) {
$result = DB :: query ( " SELECT last_value FROM \" { $table } _ID_seq \" ; " );
$row = $result -> first ();
return $row [ 'last_value' ];
}
/**
* OBSOLETE : Get the ID for the next new record for the table .
*
* @ var string $table The name od the table .
* @ return int
*/
public function getNextID ( $table ) {
user_error ( 'getNextID is OBSOLETE (and will no longer work properly)' , E_USER_WARNING );
$result = $this -> query ( " SELECT MAX(ID)+1 FROM \" $table\ " " )->value();
return $result ? $result : 1 ;
}
public function isActive () {
return $this -> active ? true : false ;
}
public function createDatabase () {
$this -> query ( " CREATE DATABASE $this->database " );
//$this->query("USE $this->database");
//$this->tableList = $this->fieldList = $this->indexList = null;
//if(mysql_select_db($this->database, $this->dbConn)) {
// $this->active = true;
// return true;
//}
}
/**
* Drop the database that this object is currently connected to .
* Use with caution .
*/
public function dropDatabase () {
$this -> query ( " DROP DATABASE $this->database " );
}
/**
* Returns the name of the currently selected database
*/
public function currentDatabase () {
return $this -> database ;
}
/**
* Switches to the given database .
* If the database doesn ' t exist , you should call createDatabase () after calling selectDatabase ()
*/
public function selectDatabase ( $dbname ) {
$this -> database = $dbname ;
if ( $this -> databaseExists ( $this -> database )) mysql_select_db ( $this -> database , $this -> dbConn );
$this -> tableList = $this -> fieldList = $this -> indexList = null ;
}
/**
* Returns true if the named database exists .
*/
public function databaseExists ( $name ) {
$SQL_name = Convert :: raw2sql ( $name );
return $this -> query ( " SHOW DATABASES LIKE ' $SQL_name ' " ) -> value () ? true : false ;
}
public function createTable ( $tableName , $fields = null , $indexes = null ) {
$fieldSchemas = $indexSchemas = " " ;
if ( $fields ) foreach ( $fields as $k => $v ) $fieldSchemas .= " \" $k\ " $v , \n " ;
//if($indexes) foreach($indexes as $k => $v) $indexSchemas .= $this->getIndexSqlDefinition($k, $v) . ",\n";
//we need to generate indexes like this: CREATE INDEX IX_vault_to_export ON vault (to_export);
//If we have a fulltext search request, then we need to create a special column
//for GiST searches
echo 'creating table with these indexes:<pre>' ;
print_r ( $indexes );
echo '</pre>' ;
$fulltexts = '' ;
foreach ( $indexes as $this_index ){
if ( $this_index [ 'type' ] == 'fulltext' ){
//ALTER TABLE tblMessages ADD COLUMN idxFTI tsvector;
//CREATE INDEX ix_vault_indexed_words ON vault_indexed USING gist(words);
2009-01-09 04:43:28 +01:00
//$fulltexts.=$this_index['name'] . ' tsvector, ';
2008-11-23 02:20:39 +01:00
}
}
if ( $indexes ) foreach ( $indexes as $k => $v ) $indexSchemas .= $this -> getIndexSqlDefinition ( $tableName , $k , $v ) . " \n " ;
$this -> query ( " CREATE TABLE \" $tableName\ " (
\ " ID \" SERIAL8 NOT NULL,
$fieldSchemas
$fulltexts
primary key ( \ " ID \" )
); $indexSchemas " );
}
/**
* Alter a table ' s schema .
* @ param $table The name of the table to alter
* @ param $newFields New fields , a map of field name => field schema
* @ param $newIndexes New indexes , a map of index name => index type
* @ param $alteredFields Updated fields , a map of field name => field schema
* @ param $alteredIndexes Updated indexes , a map of index name => index type
*/
public function alterTable ( $tableName , $newFields = null , $newIndexes = null , $alteredFields = null , $alteredIndexes = null ) {
$fieldSchemas = $indexSchemas = " " ;
2008-11-24 00:29:09 +01:00
$alterList = array ();
2008-11-23 02:20:39 +01:00
if ( $newFields ) foreach ( $newFields as $k => $v ) $alterList [] .= " ADD \" $k\ " $v " ;
2008-11-24 00:29:09 +01:00
//if($newIndexes) foreach($newIndexes as $k => $v) $alterList[] .= "ADD " . $this->getIndexSqlDefinition($tableName, $k, $v);
2008-11-23 02:20:39 +01:00
if ( $alteredFields ) foreach ( $alteredFields as $k => $v ) $alterList [] .= " CHANGE \" $k\ " \ " $k\ " $v " ;
2008-11-24 00:29:09 +01:00
/*
2008-11-23 02:20:39 +01:00
echo 'alterations:<pre>' ;
print_r ( $newFields );
echo '</pre>' ;
2008-11-24 00:29:09 +01:00
*/
2008-11-23 02:20:39 +01:00
//DB ABSTRACTION: we need to change the constraints to be a separate 'add' command,
//see http://www.postgresql.org/docs/8.1/static/sql-altertable.html
if ( $alteredIndexes ) foreach ( $alteredIndexes as $k => $v ) {
$alterList [] .= " DROP INDEX \" $k\ " " ;
$alterList [] .= " ADD " . $this -> getIndexSqlDefinition ( $tableName , $k , $v );
}
2008-11-24 00:29:09 +01:00
if ( $alterList ) {
$alterations = implode ( " , \n " , $alterList );
$this -> query ( " ALTER TABLE \" $tableName\ " " . $alterations );
}
2008-11-23 02:20:39 +01:00
}
public function renameTable ( $oldTableName , $newTableName ) {
$this -> query ( " ALTER TABLE \" $oldTableName\ " RENAME \ " $newTableName\ " " );
}
/**
* Checks a table ' s integrity and repairs it if necessary .
* @ var string $tableName The name of the table .
* @ return boolean Return true if the table has integrity after the method is complete .
*/
public function checkAndRepairTable ( $tableName ) {
//if(!$this->runTableCheckCommand("VACUUM FULL \"$tableName\"")) {
// Database::alteration_message("Table $tableName: repaired","repaired");
// return $this->runTableCheckCommand("REPAIR TABLE \"$tableName\" USE_FRM");
//} else {
// return true;
//}
$this -> runTableCheckCommand ( " VACUUM FULL \" $tableName\ " " );
return true ;
}
/**
* Helper function used by checkAndRepairTable .
* @ param string $sql Query to run .
* @ return boolean Returns if the query returns a successful result .
*/
protected function runTableCheckCommand ( $sql ) {
$testResults = $this -> query ( $sql );
foreach ( $testResults as $testRecord ) {
if ( strtolower ( $testRecord [ 'Msg_text' ]) != 'ok' ) {
return false ;
}
}
return true ;
}
public function createField ( $tableName , $fieldName , $fieldSpec ) {
$this -> query ( " ALTER TABLE \" $tableName\ " ADD \ " $fieldName\ " $fieldSpec " );
}
/**
* Change the database type of the given field .
* @ param string $tableName The name of the tbale the field is in .
* @ param string $fieldName The name of the field to change .
* @ param string $fieldSpec The new field specification
*/
public function alterField ( $tableName , $fieldName , $fieldSpec ) {
$this -> query ( " ALTER TABLE \" $tableName\ " CHANGE \ " $fieldName\ " \ " $fieldName\ " $fieldSpec " );
}
2008-11-24 00:29:09 +01:00
/**
* Change the database column name of the given field .
*
* @ param string $tableName The name of the tbale the field is in .
* @ param string $oldName The name of the field to change .
* @ param string $newName The new name of the field
*/
public function renameField ( $tableName , $oldName , $newName ) {
$fieldList = $this -> fieldList ( $tableName );
if ( array_key_exists ( $oldName , $fieldList )) {
$this -> query ( " ALTER TABLE \" $tableName\ " CHANGE \ " $oldName\ " \ " $newName\ " " . $fieldList[$oldName] );
}
}
2008-11-23 02:20:39 +01:00
public function fieldList ( $table ) {
2008-11-24 00:29:09 +01:00
$fields = $this -> query ( " SELECT b.attname FROM pg_class a
INNER JOIN pg_attribute b ON a . relfilenode = b . attrelid
WHERE a . relname = '$table'
AND NOT b . attisdropped AND b . attnum > 0 " )->column();
$output = array ();
2008-11-24 00:52:06 +01:00
if ( $fields ) foreach ( $fields as $field ) {
2008-11-24 00:29:09 +01:00
$output [ $field ] = true ;
2008-11-23 02:20:39 +01:00
}
2008-11-24 00:29:09 +01:00
return $output ;
2008-11-23 02:20:39 +01:00
}
/**
* Create an index on a table .
* @ param string $tableName The name of the table .
* @ param string $indexName The name of the index .
* @ param string $indexSpec The specification of the index , see Database :: requireIndex () for more details .
*/
public function createIndex ( $tableName , $indexName , $indexSpec ) {
//$this->query("ALTER TABLE \"$tableName\" ADD " . $this->getIndexSqlDefinition($indexName, $indexSpec));
$this -> query ( $this -> getIndexSqlDefinition ( $tableName , $indexName , $indexSpec ));
}
2009-01-09 05:01:37 +01:00
/*
* This takes the index spec which has been provided by a class ( ie static $indexes = blah blah )
* and turns it into a proper string .
* Some indexes may be arrays , such as fulltext and unique indexes , and this allows database - specific
* arrays to be created .
*/
public function convertIndexSpec ( $indexSpec ){
if ( is_array ( $indexSpec )){
//Here we create a db-specific version of whatever index we need to create.
switch ( $indexSpec [ 'type' ]){
case 'fulltext' :
$indexSpec = 'fulltext (' . str_replace ( ' ' , '' , $indexSpec [ 'value' ]) . ')' ;
break ;
case 'unique' :
$indexSpec = 'unique (' . $indexSpec [ 'value' ] . ')' ;
break ;
}
}
//return $indexSpec;
return '' ;
}
2008-11-23 02:20:39 +01:00
protected function getIndexSqlDefinition ( $tableName , $indexName , $indexSpec ) {
//$indexSpec = trim($indexSpec);
//if($indexSpec[0] != '(') list($indexType, $indexFields) = explode(' ',$indexSpec,2);
//else $indexFields = $indexSpec;
//if(!isset($indexType)) {
// $indexType = 'create index';
//}
//CREATE INDEX IX_vault_to_export ON vault (to_export);
echo 'index spec:<pre>' ;
print_r ( $indexSpec );
echo '</pre>' ;
if ( ! isset ( $indexSpec [ 'type' ])){
//It's not the best method, but to keep things simple, we've allowed unique indexes to be
//specified as inline strings. So now we need to detect 'unique (' as the first word, and
//do things differently if that's the case.
//The alternative is to force unique indexes to adopt the complex index method (below)
$indexSpec = $indexSpec [ 'sql' ];
$unique = '' ;
if ( substr ( $indexSpec , 0 , 8 ) == 'unique (' ){
$unique = 'unique ' ;
$indexSpec = substr ( $indexSpec , 8 );
}
$indexSpec = trim ( $indexSpec , '()' );
$bits = explode ( ',' , $indexSpec );
$indexes = " \" " . implode ( " \" , \" " , $bits ) . " \" " ;
echo 'the indexes are ' . $indexes . '<br>' ;
return 'create ' . $unique . 'index ix_' . $tableName . '_' . $indexName . " ON \" " . $tableName . " \" ( " . $indexes . ');' ;
} else {
//create a type-specific index
if ( $indexSpec [ 'type' ] == 'fulltext' ){
2009-01-09 04:43:28 +01:00
//return 'create index ix_' . $tableName . '_' . $indexSpec['name'] . " ON \"" . $tableName . "\" USING gist(" . $indexSpec['name'] . ');';
return '' ;
2008-11-23 02:20:39 +01:00
}
}
//return "$indexType \"$indexName\" $indexFields";
}
/**
* Alter an index on a table .
* @ param string $tableName The name of the table .
* @ param string $indexName The name of the index .
* @ param string $indexSpec The specification of the index , see Database :: requireIndex () for more details .
*/
public function alterIndex ( $tableName , $indexName , $indexSpec ) {
$indexSpec = trim ( $indexSpec );
if ( $indexSpec [ 0 ] != '(' ) {
list ( $indexType , $indexFields ) = explode ( ' ' , $indexSpec , 2 );
} else {
$indexFields = $indexSpec ;
}
if ( ! $indexType ) {
$indexType = " index " ;
}
//$this->query("ALTER TABLE \"$tableName\" DROP INDEX \"$indexName\"");
$this -> query ( " DROP INDEX $indexName " );
$this -> query ( " ALTER TABLE \" $tableName\ " ADD $indexType \ " $indexName\ " $indexFields " );
}
/**
* Return the list of indexes in a table .
* @ param string $table The table name .
* @ return array
*/
public function indexList ( $table ) {
/* $indexes = DB :: query ( " SHOW INDEXES IN \" $table\ " " );
foreach ( $indexes as $index ) {
$groupedIndexes [ $index [ 'Key_name' ]][ 'fields' ][ $index [ 'Seq_in_index' ]] = $index [ 'Column_name' ];
if ( $index [ 'Index_type' ] == 'FULLTEXT' ) {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = 'fulltext ' ;
} else if ( ! $index [ 'Non_unique' ]) {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = 'unique ' ;
} else {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = '' ;
}
}
foreach ( $groupedIndexes as $index => $details ) {
ksort ( $details [ 'fields' ]);
$indexList [ $index ] = $details [ 'type' ] . '(' . implode ( ',' , $details [ 'fields' ]) . ')' ;
}
return $indexList ; */
//Obtained by starting postgres with the -E option:
$indexes = DB :: query ( " SELECT c.relname as \" Name \" ,
CASE c . relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' END as \ " Type \" ,
r . rolname as \ " Owner \" ,
c2 . relname as \ " Table \"
FROM pg_catalog . pg_class c
JOIN pg_catalog . pg_roles r ON r . oid = c . relowner
LEFT JOIN pg_catalog . pg_namespace n ON n . oid = c . relnamespace
LEFT JOIN pg_catalog . pg_index i ON i . indexrelid = c . oid
LEFT JOIN pg_catalog . pg_class c2 ON i . indrelid = c2 . oid
WHERE c . relkind IN ( 'i' , '' )
AND n . nspname <> 'pg_catalog'
AND n . nspname !~ '^pg_toast'
AND pg_catalog . pg_table_is_visible ( c . oid )
AND c2 . relname = '$table' AND c . relkind = 'index' ; " );
//DB ABSTRACTION: TODO: we are not getting actual index information here, just the basic existence stuff:
foreach ( $indexes as $index ) {
/* $groupedIndexes [ $index [ 'Key_name' ]][ 'fields' ][ $index [ 'Seq_in_index' ]] = $index [ 'Column_name' ];
if ( $index [ 'Index_type' ] == 'FULLTEXT' ) {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = 'fulltext ' ;
} else if ( ! $index [ 'Non_unique' ]) {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = 'unique ' ;
} else {
$groupedIndexes [ $index [ 'Key_name' ]][ 'type' ] = '' ;
} */
$indexList [ $index [ 'Name' ]] = $index [ 'Name' ];
}
//foreach($groupedIndexes as $index => $details) {
//ksort($details['fields']);
//$indexList[$index] = $details['type'] . '(' . implode(',',$details['fields']) . ')';
//}
//echo 'index list: <pre>';
//print_r($indexList);
//echo '</pre>';
2008-11-24 00:29:09 +01:00
/*
2008-11-23 02:20:39 +01:00
echo " <p style='color:red; font-weight: bold;'>INDEX LIST TRIGGERED (LINE 375 POSTGRESQLDATABASE.PHP</p> " ;
2008-11-24 00:29:09 +01:00
*/
2008-11-23 02:20:39 +01:00
return $indexList ;
}
/**
* Returns a list of all the tables in the database .
* Table names will all be in lowercase .
* @ return array
*/
public function tableList () {
foreach ( $this -> query ( " SELECT tablename FROM pg_tables WHERE tablename NOT ILIKE 'pg_%' AND tablename NOT ILIKE 'sql_%' " ) as $record ) {
$table = strtolower ( reset ( $record ));
$tables [ $table ] = $table ;
}
return isset ( $tables ) ? $tables : null ;
}
/**
* Return the number of rows affected by the previous operation .
* @ return int
*/
public function affectedRows () {
return pg_affected_rows ( DB :: $lastQuery );
}
/**
* A function to return the field names and datatypes for the particular table
*/
public function tableDetails ( $tableName ){
$query = " SELECT a.attname as \" Column \" , pg_catalog.format_type(a.atttypid, a.atttypmod) as \" Datatype \" FROM pg_catalog.pg_attribute a WHERE a.attnum > 0 AND NOT a.attisdropped AND a.attrelid = ( SELECT c.oid FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relname ~ '^( $tableName ) $ ' AND pg_catalog.pg_table_is_visible(c.oid)); " ;
$result = DB :: query ( $query );
$table = Array ();
while ( $row = pg_fetch_assoc ( $result )){
$table [] = Array ( 'Column' => $row [ 'Column' ], 'DataType' => $row [ 'DataType' ]);
}
return $table ;
}
/**
* Return a boolean type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function boolean ( $values ){
//Annoyingly, we need to do a good old fashioned switch here:
( $values [ 'default' ]) ? $default = 'true' : $default = 'false' ;
return 'boolean not null default ' . $default ;
}
/**
* Return a date type - formatted string
* For MySQL , we simply return the word 'date' , no other parameters are necessary
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function date ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'date');
//DB::requireField($this->tableName, $this->name, "date");
return 'date' ;
}
/**
* Return a decimal type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function decimal ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'decimal', 'precision'=>"$this->wholeSize,$this->decimalSize");
//DB::requireField($this->tableName, $this->name, "decimal($this->wholeSize,$this->decimalSize)");
return 'decimal(' . ( int ) $values [ 'precision' ] . ')' ;
}
/**
* Return a enum type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function enum ( $values ){
//Enums are a bit different. We'll be creating a varchar(255) with a constraint of all the usual
//enum options.
//NOTE: In this one instance, we are including the table name in the values array
2008-11-24 00:29:09 +01:00
return " varchar(255) not null default ' " . $values [ 'default' ] . " ' check ( \" " . $values [ 'name' ] . " \" in (' " . implode ( '\', \'' , $values [ 'enums' ]) . " ')) " ;
2008-11-23 02:20:39 +01:00
}
/**
* Return a float type - formatted string
* For MySQL , we simply return the word 'date' , no other parameters are necessary
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function float ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'float');
//DB::requireField($this->tableName, $this->name, "float");
return 'float' ;
}
/**
* Return a int type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function int ( $values ){
//We'll be using an 8 digit precision to keep it in line with the serial8 datatype for ID columns
return 'numeric(8) not null default ' . ( int ) $values [ 'default' ];
}
/**
* Return a datetime type - formatted string
* For PostgreSQL , we simply return the word 'timestamp' , no other parameters are necessary
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function ssdatetime ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'datetime');
//DB::requireField($this->tableName, $this->name, $values);
return 'timestamp' ;
}
/**
* Return a text type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function text ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'mediumtext', 'character set'=>'utf8', 'collate'=>'utf8_general_ci');
//DB::requireField($this->tableName, $this->name, "mediumtext character set utf8 collate utf8_general_ci");
return 'text' ;
}
/**
* Return a time type - formatted string
* For MySQL , we simply return the word 'time' , no other parameters are necessary
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function time ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'time');
//DB::requireField($this->tableName, $this->name, "time");
return 'time' ;
}
/**
* Return a varchar type - formatted string
*
* @ params array $values Contains a tokenised list of info about this data type
* @ return string
*/
public function varchar ( $values ){
//For reference, this is what typically gets passed to this function:
//$parts=Array('datatype'=>'varchar', 'precision'=>$this->size, 'character set'=>'utf8', 'collate'=>'utf8_general_ci');
//DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate utf8_general_ci");
return 'varchar(' . $values [ 'precision' ] . ')' ;
}
function escape_character ( $escape = false ){
if ( $escape )
return " \\ \" " ;
else
return " \" " ;
}
/**
* Create a fulltext search datatype for MySQL
*
* @ param array $spec
*/
function fulltext ( $table , $spec ){
//CREATE INDEX ix_vault_indexed_words ON vault_indexed USING gist(words);
//$spec['name'] is the column we've created that holds all the words we want to index.
//This is a coalesced collection of multiple columns if necessary
$spec = 'create index ix_' . $table . '_' . $spec [ 'name' ] . ' on ' . $table . ' using gist(' . $spec [ 'name' ] . ');' ;
return $spec ;
}
2008-11-24 00:29:09 +01:00
/**
* Returns true if this table exists
* @ todo Make a proper implementation
*/
function hasTable ( $tableName ) {
return true ;
}
/**
* Return enum values for the given field
* @ todo Make a proper implementation
*/
function enumValuesForField ( $tableName , $fieldName ) {
return array ( 'SiteTree' , 'Page' );
}
/**
* Convert a SQLQuery object into a SQL statement
* @ todo There is a lot of duplication between this and MySQLDatabase :: sqlQueryToString () . Perhaps they could both call a common
* helper function in Database ?
*/
public function sqlQueryToString ( SQLQuery $sqlQuery ) {
if ( ! $sqlQuery -> from ) return '' ;
$distinct = $sqlQuery -> distinct ? " DISTINCT " : " " ;
if ( $sqlQuery -> delete ) {
$text = " DELETE " ;
} else if ( $sqlQuery -> select ) {
$text = " SELECT $distinct " . implode ( " , " , $sqlQuery -> select );
}
$text .= " FROM " . implode ( " " , $sqlQuery -> from );
if ( $sqlQuery -> where ) $text .= " WHERE ( " . $sqlQuery -> getFilter () . " ) " ;
if ( $sqlQuery -> groupby ) $text .= " GROUP BY " . implode ( " , " , $sqlQuery -> groupby );
if ( $sqlQuery -> having ) $text .= " HAVING ( " . implode ( " ) AND ( " , $sqlQuery -> having ) . " ) " ;
if ( $sqlQuery -> orderby ) $text .= " ORDER BY " . $sqlQuery -> orderby ;
if ( $sqlQuery -> limit ) {
$limit = $sqlQuery -> limit ;
// Pass limit as array or SQL string value
if ( is_array ( $limit )) {
if ( ! array_key_exists ( 'limit' , $limit )) user_error ( 'SQLQuery::limit(): Wrong format for $limit' , E_USER_ERROR );
if ( isset ( $limit [ 'start' ]) && is_numeric ( $limit [ 'start' ]) && isset ( $limit [ 'limit' ]) && is_numeric ( $limit [ 'limit' ])) {
$combinedLimit = ( int ) $limit [ 'start' ] . ',' . ( int ) $limit [ 'limit' ];
} elseif ( isset ( $limit [ 'limit' ]) && is_numeric ( $limit [ 'limit' ])) {
$combinedLimit = ( int ) $limit [ 'limit' ];
} else {
$combinedLimit = false ;
}
if ( ! empty ( $combinedLimit )) $this -> limit = $combinedLimit ;
} else {
$text .= " LIMIT " . $sqlQuery -> limit ;
}
}
return $text ;
}
2008-11-23 02:20:39 +01:00
}
/**
* A result - set from a MySQL database .
* @ package sapphire
* @ subpackage model
*/
class PostgreSQLQuery extends Query {
/**
* The MySQLDatabase object that created this result set .
* @ var MySQLDatabase
*/
private $database ;
/**
* The internal MySQL handle that points to the result set .
* @ var resource
*/
private $handle ;
/**
* Hook the result - set given into a Query class , suitable for use by sapphire .
* @ param database The database object that created this query .
* @ param handle the internal mysql handle that is points to the resultset .
*/
public function __construct ( PostgreSQLDatabase $database , $handle ) {
$this -> database = $database ;
$this -> handle = $handle ;
parent :: __construct ();
}
public function __destroy () {
//mysql_free_result($this->handle);
}
public function seek ( $row ) {
//return mysql_data_seek($this->handle, $row);
//This is unnecessary in postgres. You can just provide a row number with the fetch
//command.
}
public function numRecords () {
return pg_num_rows ( $this -> handle );
}
public function nextRecord () {
// Coalesce rather than replace common fields.
if ( $data = pg_fetch_row ( $this -> handle )) {
foreach ( $data as $columnIdx => $value ) {
$columnName = pg_field_name ( $this -> handle , $columnIdx );
// $value || !$ouput[$columnName] means that the *last* occurring value is shown
// !$ouput[$columnName] means that the *first* occurring value is shown
if ( isset ( $value ) || ! isset ( $output [ $columnName ])) {
$output [ $columnName ] = $value ;
}
}
return $output ;
} else {
return false ;
}
}
}
?>