mirror of
https://github.com/silverstripe/silverstripe-postgresql
synced 2024-10-22 17:05:45 +02:00
All the most recent changes to get unit tests working
This commit is contained in:
parent
f460491bbc
commit
b0780eeff6
@ -29,6 +29,15 @@ class PostgreSQLDatabase extends Database {
|
|||||||
*/
|
*/
|
||||||
private $database;
|
private $database;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This holds the name of the original database
|
||||||
|
* So if you switch to another for unit tests, you
|
||||||
|
* can then switch back in order to drop the temp database
|
||||||
|
*/
|
||||||
|
private $database_original;
|
||||||
|
|
||||||
|
private $parameters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a PostgreSQL database.
|
* Connect to a PostgreSQL database.
|
||||||
* @param array $parameters An map of parameters, which should include:
|
* @param array $parameters An map of parameters, which should include:
|
||||||
@ -39,22 +48,44 @@ class PostgreSQLDatabase extends Database {
|
|||||||
*/
|
*/
|
||||||
public function __construct($parameters) {
|
public function __construct($parameters) {
|
||||||
|
|
||||||
|
//We will store these connection parameters for use elsewhere (ie, unit tests)
|
||||||
|
$this->parameters=$parameters;
|
||||||
|
$this->connectDatabase();
|
||||||
|
|
||||||
|
$this->database_original=$this->database;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Uses whatever connection details are in the $parameters array to connect to a database of a given name
|
||||||
|
*/
|
||||||
|
function connectDatabase(){
|
||||||
|
|
||||||
|
$parameters=$this->parameters;
|
||||||
|
|
||||||
|
if(!$parameters)
|
||||||
|
return false;
|
||||||
|
|
||||||
($parameters['username']!='') ? $username=' user=' . $parameters['username'] : $username='';
|
($parameters['username']!='') ? $username=' user=' . $parameters['username'] : $username='';
|
||||||
($parameters['password']!='') ? $password=' password=' . $parameters['password'] : $password='';
|
($parameters['password']!='') ? $password=' password=' . $parameters['password'] : $password='';
|
||||||
|
|
||||||
|
if(!isset($this->database))
|
||||||
|
$dbName=$parameters['database'];
|
||||||
|
else $dbName=$this->database;
|
||||||
|
|
||||||
//assumes that the server and dbname will always be provided:
|
//assumes that the server and dbname will always be provided:
|
||||||
$this->dbConn = pg_connect('host=' . $parameters['server'] . ' port=5432 dbname=' . $parameters['database'] . $username . $password);
|
$this->dbConn = pg_connect('host=' . $parameters['server'] . ' port=5432 dbname=' . $dbName . $username . $password);
|
||||||
|
|
||||||
//By virtue of getting here, the connection is active:
|
//By virtue of getting here, the connection is active:
|
||||||
$this->active=true;
|
$this->active=true;
|
||||||
$this->database = $parameters['database'];
|
$this->database = $dbName;
|
||||||
|
|
||||||
if(!$this->dbConn) {
|
if(!$this->dbConn) {
|
||||||
$this->databaseError("Couldn't connect to PostgreSQL database");
|
$this->databaseError("Couldn't connect to PostgreSQL database");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::__construct();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Not implemented, needed for PDO
|
* Not implemented, needed for PDO
|
||||||
*/
|
*/
|
||||||
@ -64,14 +95,15 @@ class PostgreSQLDatabase extends Database {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if this database supports collations
|
* Returns true if this database supports collations
|
||||||
|
* TODO: get rid of this?
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function supportsCollations() {
|
public function supportsCollations() {
|
||||||
return $this->getVersion() >= 4.1;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of MySQL.
|
* The version of PostgreSQL.
|
||||||
* @var float
|
* @var float
|
||||||
*/
|
*/
|
||||||
private $pgsqlVersion;
|
private $pgsqlVersion;
|
||||||
@ -92,7 +124,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the database server, namely mysql.
|
* Get the database server, namely PostgreSQL.
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getDatabaseServer() {
|
public function getDatabaseServer() {
|
||||||
@ -100,6 +132,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function query($sql, $errorLevel = E_USER_ERROR) {
|
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'))) {
|
if(isset($_REQUEST['previewwrite']) && in_array(strtolower(substr($sql,0,strpos($sql,' '))), array('insert','update','delete','replace'))) {
|
||||||
Debug::message("Will execute: $sql");
|
Debug::message("Will execute: $sql");
|
||||||
return;
|
return;
|
||||||
@ -109,7 +142,6 @@ class PostgreSQLDatabase extends Database {
|
|||||||
$starttime = microtime(true);
|
$starttime = microtime(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//echo $sql . '<hr>';
|
|
||||||
$handle = pg_query($this->dbConn, $sql);
|
$handle = pg_query($this->dbConn, $sql);
|
||||||
|
|
||||||
if(isset($_REQUEST['showqueries'])) {
|
if(isset($_REQUEST['showqueries'])) {
|
||||||
@ -119,7 +151,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
|
|
||||||
DB::$lastQuery=$handle;
|
DB::$lastQuery=$handle;
|
||||||
|
|
||||||
if(!$handle && $errorLevel) $this->databaseError("Couldn't run query: $sql | " . pgsql_error($this->dbConn), $errorLevel);
|
if(!$handle && $errorLevel) $this->databaseError("Couldn't run query: $sql | " . pg_last_error($this->dbConn), $errorLevel);
|
||||||
|
|
||||||
return new PostgreSQLQuery($this, $handle);
|
return new PostgreSQLQuery($this, $handle);
|
||||||
}
|
}
|
||||||
@ -146,8 +178,16 @@ class PostgreSQLDatabase extends Database {
|
|||||||
return $this->active ? true : false;
|
return $this->active ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This will create a database based on whatever is in the $this->database value
|
||||||
|
* So you need to have called $this->selectDatabase() first, or used the __construct method
|
||||||
|
*/
|
||||||
public function createDatabase() {
|
public function createDatabase() {
|
||||||
|
|
||||||
$this->query("CREATE DATABASE $this->database");
|
$this->query("CREATE DATABASE $this->database");
|
||||||
|
|
||||||
|
$this->connectDatabase();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,7 +195,13 @@ class PostgreSQLDatabase extends Database {
|
|||||||
* Use with caution.
|
* Use with caution.
|
||||||
*/
|
*/
|
||||||
public function dropDatabase() {
|
public function dropDatabase() {
|
||||||
$this->query("DROP DATABASE $this->database");
|
|
||||||
|
//First, we need to switch back to the original database so we can drop the current one
|
||||||
|
$db_to_drop=$this->database;
|
||||||
|
$this->selectDatabase($this->database_original);
|
||||||
|
$this->connectDatabase();
|
||||||
|
|
||||||
|
$this->query("DROP DATABASE $db_to_drop");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,35 +216,59 @@ class PostgreSQLDatabase extends Database {
|
|||||||
* If the database doesn't exist, you should call createDatabase() after calling selectDatabase()
|
* If the database doesn't exist, you should call createDatabase() after calling selectDatabase()
|
||||||
*/
|
*/
|
||||||
public function selectDatabase($dbname) {
|
public function selectDatabase($dbname) {
|
||||||
$this->database = $dbname;
|
//$this->database = $dbname;
|
||||||
if($this->databaseExists($this->database)) mysql_select_db($this->database, $this->dbConn);
|
//if($this->databaseExists($this->database)) mysql_select_db($this->database, $this->dbConn);
|
||||||
|
//if($this->databaseExists($this->database)) pg_d
|
||||||
|
$this->database=$dbname;
|
||||||
|
|
||||||
$this->tableList = $this->fieldList = $this->indexList = null;
|
$this->tableList = $this->fieldList = $this->indexList = null;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the named database exists.
|
* Returns true if the named database exists.
|
||||||
*/
|
*/
|
||||||
public function databaseExists($name) {
|
public function databaseExists($name) {
|
||||||
//TODO: fix me to test for the existance of the database
|
$SQL_name=Convert::raw2sql($name);
|
||||||
//For the moment, this always returns true
|
$result=$this->query("SELECT datname FROM pg_database WHERE datname='$SQL_name';")->first();
|
||||||
//$SQL_name = Convert::raw2sql($name);
|
return $this->query("SELECT datname FROM pg_database WHERE datname='$SQL_name';")->first() ? true : false;
|
||||||
//return $this->query("SHOW DATABASES LIKE '$SQL_name'")->value() ? true : false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createTable($tableName, $fields = null, $indexes = null, $options = null) {
|
public function createTable($tableName, $fields = null, $indexes = null, $options = null) {
|
||||||
$fieldSchemas = $indexSchemas = "";
|
$fieldSchemas = $indexSchemas = "";
|
||||||
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
|
if($fields) foreach($fields as $k => $v) $fieldSchemas .= "\"$k\" $v,\n";
|
||||||
$addOptions = (isset($options[$this->class])) ? $options[$this->class] : null;
|
if(isset($this->class)){
|
||||||
|
$addOptions = (isset($options[$this->class])) ? $options[$this->class] : null;
|
||||||
|
} else $addOptions=null;
|
||||||
|
|
||||||
|
//First of all, does this table already exist
|
||||||
|
$doesExist=$this->TableExists($tableName);
|
||||||
|
if($doesExist)
|
||||||
|
return false;
|
||||||
|
|
||||||
//If we have a fulltext search request, then we need to create a special column
|
//If we have a fulltext search request, then we need to create a special column
|
||||||
//for GiST searches
|
//for GiST searches
|
||||||
$fulltexts='';
|
$fulltexts='';
|
||||||
|
$triggers='';
|
||||||
foreach($indexes as $name=>$this_index){
|
foreach($indexes as $name=>$this_index){
|
||||||
if($this_index['type']=='fulltext'){
|
if($this_index['type']=='fulltext'){
|
||||||
//For full text search, we need to create a column for the index
|
|
||||||
$fulltexts .= "\"ts_$name\" tsvector, ";
|
|
||||||
|
|
||||||
|
//For full text search, we need to create a column for the index
|
||||||
|
$columns=explode(',', $this_index['value']);
|
||||||
|
for($i=0; $i<sizeof($columns);$i++)
|
||||||
|
$columns[$i]="\"" . trim($columns[$i]) . "\"";
|
||||||
|
|
||||||
|
$columns=implode(', ', $columns);
|
||||||
|
|
||||||
|
$fulltexts .= "\"ts_$name\" tsvector, ";
|
||||||
|
$triggerName="ts_{$tableName}_{$name}";
|
||||||
|
|
||||||
|
$this->dropTrigger($triggerName, $tableName);
|
||||||
|
$triggers.="CREATE TRIGGER $triggerName BEFORE INSERT OR UPDATE
|
||||||
|
ON \"$tableName\" FOR EACH ROW EXECUTE PROCEDURE
|
||||||
|
tsvector_update_trigger(\"ts_$name\", 'pg_catalog.english', $columns);";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +279,10 @@ class PostgreSQLDatabase extends Database {
|
|||||||
$fulltexts
|
$fulltexts
|
||||||
primary key (\"ID\")
|
primary key (\"ID\")
|
||||||
); $indexSchemas $addOptions");
|
); $indexSchemas $addOptions");
|
||||||
|
|
||||||
|
if($triggers!=''){
|
||||||
|
$this->query($triggers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,17 +312,32 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//see http://www.postgresql.org/docs/8.1/static/sql-altertable.html
|
//see http://www.postgresql.org/docs/8.1/static/sql-altertable.html
|
||||||
$alterIndexList=Array();
|
$alterIndexList=Array();
|
||||||
if($alteredIndexes) foreach($alteredIndexes as $v) {
|
if($alteredIndexes) foreach($alteredIndexes as $v) {
|
||||||
if(is_array($v))
|
//We are only going to delete indexes which exist
|
||||||
$alterIndexList[] = 'DROP INDEX ix_' . strtolower($tableName) . '_' . strtolower($v['value']) . ';';
|
$indexes=$this->indexList($tableName);
|
||||||
else
|
|
||||||
$alterIndexList[] = 'DROP INDEX ix_' . strtolower($tableName) . '_' . strtolower(trim($v, '()')) . ';';
|
if(isset($indexes[$v['value']])){
|
||||||
|
if(is_array($v))
|
||||||
$k=$v['value'];
|
$alterIndexList[] = 'DROP INDEX ix_' . strtolower($tableName) . '_' . strtolower($v['value']) . ';';
|
||||||
$alterIndexList[] .= $this->getIndexSqlDefinition($tableName, $k, $v);
|
else
|
||||||
|
$alterIndexList[] = 'DROP INDEX ix_' . strtolower($tableName) . '_' . strtolower(trim($v, '()')) . ';';
|
||||||
|
|
||||||
|
$k=$v['value'];
|
||||||
|
$alterIndexList[] .= $this->getIndexSqlDefinition($tableName, $k, $v);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add the new indexes:
|
//Add the new indexes:
|
||||||
if($newIndexes) foreach($newIndexes as $k=>$v){
|
if($newIndexes) foreach($newIndexes as $k=>$v){
|
||||||
|
//Check that this index doesn't already exist:
|
||||||
|
$indexes=$this->indexList($tableName);
|
||||||
|
if(isset($indexes[trim($v, '()')])){
|
||||||
|
if(is_array($v)){
|
||||||
|
$alterIndexList[] = 'DROP INDEX ix_' . strtolower($tableName) . '_' . strtolower($v['value']) . ';';
|
||||||
|
} else {
|
||||||
|
$alterIndexList[] = 'DROP INDEX ' . $indexes[trim($v, '()')]['indexname'] . ';';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$alterIndexList[] = $this->getIndexSqlDefinition($tableName, $k, $v);
|
$alterIndexList[] = $this->getIndexSqlDefinition($tableName, $k, $v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +379,9 @@ class PostgreSQLDatabase extends Database {
|
|||||||
print_r($matches);
|
print_r($matches);
|
||||||
echo '</pre>';
|
echo '</pre>';
|
||||||
}*/
|
}*/
|
||||||
|
if(sizeof($matches)==0)
|
||||||
|
return '';
|
||||||
|
|
||||||
if($matches[1]=='serial8')
|
if($matches[1]=='serial8')
|
||||||
return '';
|
return '';
|
||||||
|
|
||||||
@ -384,6 +475,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
case 'character varying':
|
case 'character varying':
|
||||||
//Check to see if there's a constraint attached to this column:
|
//Check to see if there's a constraint attached to this column:
|
||||||
$constraint=$this->query("SELECT conname,pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE r.contype = 'c' AND conname='" . $table . '_' . $field['column_name'] . "_check' ORDER BY 1;")->first();
|
$constraint=$this->query("SELECT conname,pg_catalog.pg_get_constraintdef(r.oid, true) FROM pg_catalog.pg_constraint r WHERE r.contype = 'c' AND conname='" . $table . '_' . $field['column_name'] . "_check' ORDER BY 1;")->first();
|
||||||
|
$enum='';
|
||||||
if($constraint){
|
if($constraint){
|
||||||
//Now we need to break this constraint text into bits so we can see what we have:
|
//Now we need to break this constraint text into bits so we can see what we have:
|
||||||
//Examples:
|
//Examples:
|
||||||
@ -418,12 +510,15 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//Get the default:
|
//Get the default:
|
||||||
//TODO: perhaps pass this to the enum function so we can
|
//TODO: perhaps pass this to the enum function so we can
|
||||||
$default=trim(substr($field['column_default'], 0, strpos($field['column_default'], '::')), "'");
|
$default=trim(substr($field['column_default'], 0, strpos($field['column_default'], '::')), "'");
|
||||||
//$field['data_type']="varchar(255) not null default '" . $default . "' check (\"" . $field['column_name'] . "\" in ('" . implode("', '", $constraints) . "'))";
|
$output[$field['column_name']]=$this->enum(Array('default'=>$default, 'name'=>$field['column_name'], 'enums'=>$constraints));
|
||||||
$field['data_type']=$this->enum(Array('default'=>$default, 'name'=>$field['column_name'], 'enums'=>$constraints));
|
|
||||||
}
|
}
|
||||||
|
} else{
|
||||||
|
$output[$field['column_name']]='varchar(' . $field['character_maximum_length'] . ')';
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
$output[$field['column_name']]=$field;
|
case 'numeric':
|
||||||
|
$output[$field['column_name']]='numeric(' . $field['numeric_precision'] . ')';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
$output[$field['column_name']] = $field;
|
$output[$field['column_name']] = $field;
|
||||||
@ -457,8 +552,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//Here we create a db-specific version of whatever index we need to create.
|
//Here we create a db-specific version of whatever index we need to create.
|
||||||
switch($indexSpec['type']){
|
switch($indexSpec['type']){
|
||||||
case 'fulltext':
|
case 'fulltext':
|
||||||
//$indexSpec='fulltext (' . str_replace(' ', '', $indexSpec['value']) . ')';
|
$indexSpec='(ts_' . $indexSpec['name'] . ')';
|
||||||
$indexSpec='(ts_' . $indexSpec['indexName'] . ')';
|
|
||||||
break;
|
break;
|
||||||
case 'unique':
|
case 'unique':
|
||||||
$indexSpec='unique (' . $indexSpec['value'] . ')';
|
$indexSpec='unique (' . $indexSpec['value'] . ')';
|
||||||
@ -479,46 +573,65 @@ class PostgreSQLDatabase extends Database {
|
|||||||
|
|
||||||
protected function getIndexSqlDefinition($tableName, $indexName, $indexSpec, $asDbValue=false) {
|
protected function getIndexSqlDefinition($tableName, $indexName, $indexSpec, $asDbValue=false) {
|
||||||
|
|
||||||
|
//TODO: create fill factor support and table partition support
|
||||||
|
//TODO: create clustering options
|
||||||
|
|
||||||
if(!$asDbValue){
|
if(!$asDbValue){
|
||||||
|
|
||||||
|
$tableCol= 'ix_' . $tableName . '_' . $indexName;
|
||||||
|
if(strlen($tableCol)>64){
|
||||||
|
$tableCol=substr($indexName, 0, 59) . rand(1000, 9999);
|
||||||
|
//echo 'it is now ' . $tableCol . "\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
if(!is_array($indexSpec)){
|
if(!is_array($indexSpec)){
|
||||||
$indexSpec=trim($indexSpec, '()');
|
$indexSpec=trim($indexSpec, '()');
|
||||||
$bits=explode(',', $indexSpec);
|
$bits=explode(',', $indexSpec);
|
||||||
$indexes="\"" . implode("\",\"", $bits) . "\"";
|
$indexes="\"" . implode("\",\"", $bits) . "\"";
|
||||||
|
|
||||||
|
$indexSpec=$this->indexList($tableName);
|
||||||
|
|
||||||
return 'create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" (" . $indexes . ");";
|
return "create index $tableCol ON \"" . $tableName . "\" (" . $indexes . ");";
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
//Misc options first:
|
||||||
|
$fillfactor=$where='';;
|
||||||
|
if(isset($indexSpec['fillfactor']))
|
||||||
|
$fillfactor='WITH (FILLFACTOR = ' . $indexSpec['fillfactor'] . ')';
|
||||||
|
if(isset($indexSpec['where']))
|
||||||
|
$where='WHERE ' . $indexSpec['where'];
|
||||||
|
|
||||||
|
//TODO: create tablespace and where clause support
|
||||||
|
|
||||||
//create a type-specific index
|
//create a type-specific index
|
||||||
|
//NOTE: hash should be removed. This is only here to demonstrate how other indexes can be made
|
||||||
switch($indexSpec['type']){
|
switch($indexSpec['type']){
|
||||||
case 'fulltext':
|
case 'fulltext':
|
||||||
$spec='create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" USING gist(\"ts_" . $indexName . "\");";
|
$spec="create index $tableCol ON \"" . $tableName . "\" USING gist(\"ts_" . $indexName . "\") $fillfactor $where";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'unique':
|
case 'unique':
|
||||||
$spec='create unique index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" (\"" . $indexSpec['value'] . "\");";
|
$spec="create unique index $tableCol ON \"" . $tableName . "\" (\"" . $indexSpec['value'] . "\") $fillfactor $where";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'btree':
|
case 'btree':
|
||||||
$spec='create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" USING btree (\"" . $indexSpec['value'] . "\");";
|
$spec="create index $tableCol ON \"" . $tableName . "\" USING btree (\"" . $indexSpec['value'] . "\") $fillfactor $where";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'hash':
|
case 'hash':
|
||||||
$spec='create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" USING hash (\"" . $indexSpec['value'] . "\");";
|
//NOTE: this is not a recommended index type
|
||||||
break;
|
$spec="create index $tableCol ON \"" . $tableName . "\" USING hash (\"" . $indexSpec['value'] . "\") $fillfactor $where";
|
||||||
|
|
||||||
case 'rtree':
|
|
||||||
$spec='create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" USING rtree (\"" . $indexSpec['value'] . "\");";
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
$spec='create index ix_' . $tableName . '_' . $indexName . " ON \"" . $tableName . "\" (\"" . $indexSpec['value'] . "\");";
|
$spec="create index $tableCol ON \"" . $tableName . "\" (\"" . $indexSpec['value'] . "\") $fillfactor $where";
|
||||||
}
|
}
|
||||||
|
|
||||||
return $spec;
|
return trim($spec) . ';';
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$indexName=trim($indexName, '()');
|
$indexName=trim($indexName, '()');
|
||||||
|
|
||||||
return $indexName;
|
return $indexName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -559,6 +672,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//Retrieve a list of indexes for the specified table
|
//Retrieve a list of indexes for the specified table
|
||||||
$indexes=DB::query("SELECT tablename, indexname, indexdef FROM pg_indexes WHERE tablename='$table';");
|
$indexes=DB::query("SELECT tablename, indexname, indexdef FROM pg_indexes WHERE tablename='$table';");
|
||||||
|
|
||||||
|
$indexList=Array();
|
||||||
foreach($indexes as $index) {
|
foreach($indexes as $index) {
|
||||||
//We don't actually need the entire created command, just a few bits:
|
//We don't actually need the entire created command, just a few bits:
|
||||||
$prefix='';
|
$prefix='';
|
||||||
@ -591,7 +705,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return isset($indexList) ? $indexList : null;
|
return isset($indexList) ? $indexList : null;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,10 +716,23 @@ class PostgreSQLDatabase extends Database {
|
|||||||
*/
|
*/
|
||||||
public function tableList() {
|
public function tableList() {
|
||||||
foreach($this->query("SELECT tablename FROM pg_tables WHERE tablename NOT ILIKE 'pg_%' AND tablename NOT ILIKE 'sql_%'") as $record) {
|
foreach($this->query("SELECT tablename FROM pg_tables WHERE tablename NOT ILIKE 'pg_%' AND tablename NOT ILIKE 'sql_%'") as $record) {
|
||||||
$table = strtolower(reset($record));
|
//$table = strtolower(reset($record));
|
||||||
|
$table = reset($record);
|
||||||
$tables[$table] = $table;
|
$tables[$table] = $table;
|
||||||
}
|
}
|
||||||
return isset($tables) ? $tables : null;
|
|
||||||
|
//Return an empty array if there's nothing in this database
|
||||||
|
return isset($tables) ? $tables : Array();
|
||||||
|
}
|
||||||
|
|
||||||
|
function TableExists($tableName){
|
||||||
|
$result=$this->query("SELECT tablename FROM pg_tables WHERE tablename='$tableName';")->first();
|
||||||
|
|
||||||
|
if($result)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -631,6 +758,18 @@ class PostgreSQLDatabase extends Database {
|
|||||||
return $table;
|
return $table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pass a legit trigger name and it will be dropped
|
||||||
|
* This assumes that the trigger has been named in a unique fashion
|
||||||
|
*/
|
||||||
|
function dropTrigger($triggerName, $tableName){
|
||||||
|
|
||||||
|
$exists=DB::query("SELECT tgname FROM pg_trigger WHERE tgname='$triggerName';")->first();
|
||||||
|
if($exists){
|
||||||
|
DB::query("DROP trigger $triggerName ON \"$tableName\";");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a boolean type-formatted string
|
* Return a boolean type-formatted string
|
||||||
*
|
*
|
||||||
@ -681,8 +820,8 @@ class PostgreSQLDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($asDbValue)
|
if($asDbValue)
|
||||||
return Array('data_type'=>'numeric', 'numeric_precision'=>'9');
|
return Array('data_type'=>'numeric', 'precision'=>'9');
|
||||||
else return 'decimal(' . $precision . ') not null';
|
else return 'decimal(' . $precision . ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -695,7 +834,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//Enums are a bit different. We'll be creating a varchar(255) with a constraint of all the usual enum options.
|
//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
|
//NOTE: In this one instance, we are including the table name in the values array
|
||||||
|
|
||||||
return "varchar(255) not null default '" . $values['default'] . "' check (\"" . $values['name'] . "\" in ('" . implode('\', \'', $values['enums']) . "'))";
|
return "varchar(255) default '" . $values['default'] . "' check (\"" . $values['name'] . "\" in ('" . implode('\', \'', $values['enums']) . "'))";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,11 +863,10 @@ class PostgreSQLDatabase extends Database {
|
|||||||
*/
|
*/
|
||||||
public function int($values, $asDbValue=false){
|
public function int($values, $asDbValue=false){
|
||||||
//We'll be using an 8 digit precision to keep it in line with the serial8 datatype for ID columns
|
//We'll be using an 8 digit precision to keep it in line with the serial8 datatype for ID columns
|
||||||
|
|
||||||
if($asDbValue)
|
if($asDbValue)
|
||||||
return Array('data_type'=>'numeric', 'numeric_precision'=>'8');
|
return Array('data_type'=>'numeric', 'precision'=>$values['precision']);
|
||||||
else
|
else
|
||||||
return 'numeric(8) not null default ' . (int)$values['default'];
|
return 'numeric(11) not null default ' . (int)$values['default'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -791,8 +929,11 @@ class PostgreSQLDatabase extends Database {
|
|||||||
//For reference, this is what typically gets passed to this function:
|
//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');
|
//$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");
|
//DB::requireField($this->tableName, $this->name, "varchar($this->size) character set utf8 collate utf8_general_ci");
|
||||||
|
if(!isset($values['precision']))
|
||||||
|
$values['precision']=255;
|
||||||
|
|
||||||
if($asDbValue)
|
if($asDbValue)
|
||||||
return Array('data_type'=>'character varying', 'character_maximum_length'=>'255');
|
return Array('data_type'=>'varchar', 'precision'=>$values['precision']);
|
||||||
else
|
else
|
||||||
return 'varchar(' . $values['precision'] . ')';
|
return 'varchar(' . $values['precision'] . ')';
|
||||||
}
|
}
|
||||||
@ -802,7 +943,7 @@ class PostgreSQLDatabase extends Database {
|
|||||||
*/
|
*/
|
||||||
public function year($values, $asDbValue=false){
|
public function year($values, $asDbValue=false){
|
||||||
if($asDbValue)
|
if($asDbValue)
|
||||||
return Array('data_type'=>'numeric', 'numeric_precision'=>'4');
|
return Array('data_type'=>'numeric', 'precision'=>'4');
|
||||||
else return 'numeric(4)';
|
else return 'numeric(4)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -870,6 +1011,13 @@ class PostgreSQLDatabase extends Database {
|
|||||||
return 'NOW()';
|
return 'NOW()';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the database-specific version of the random() function
|
||||||
|
*/
|
||||||
|
function random(){
|
||||||
|
return 'RANDOM()';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a SQLQuery object into a SQL statement
|
* 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
|
* @todo There is a lot of duplication between this and MySQLDatabase::sqlQueryToString(). Perhaps they could both call a common
|
||||||
@ -906,7 +1054,8 @@ class PostgreSQLDatabase extends Database {
|
|||||||
if(!empty($combinedLimit)) $this->limit = $combinedLimit;
|
if(!empty($combinedLimit)) $this->limit = $combinedLimit;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$text .= " LIMIT " . $sqlQuery->limit;
|
$limit=str_replace(',', ' OFFSET ', $sqlQuery->limit);
|
||||||
|
$text .= " LIMIT " . $limit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,6 +1082,95 @@ class PostgreSQLDatabase extends Database {
|
|||||||
return str_replace('_', ',', $index);
|
return str_replace('_', ',', $index);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The core search engine configuration.
|
||||||
|
* @todo There is no result relevancy or ordering as it currently stands.
|
||||||
|
*
|
||||||
|
* @param string $keywords Keywords as a space separated string
|
||||||
|
* @return object DataObjectSet of result pages
|
||||||
|
*/
|
||||||
|
public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) {
|
||||||
|
|
||||||
|
$keywords = Convert::raw2sql(trim($keywords));
|
||||||
|
$htmlEntityKeywords = htmlentities($keywords);
|
||||||
|
|
||||||
|
/*$keywordList = explode(' ', $keywords);
|
||||||
|
if($keywordList) {
|
||||||
|
foreach($keywordList as $index => $keyword) {
|
||||||
|
$keywordList[$index] = "'{$keyword}'";
|
||||||
|
}
|
||||||
|
$keywords = implode(' AND ', $keywordList);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*$htmlEntityKeywordList = explode(' ', $htmlEntityKeywords);
|
||||||
|
if($htmlEntityKeywordList) {
|
||||||
|
foreach($htmlEntityKeywordList as $index => $keyword) {
|
||||||
|
$htmlEntityKeywordList[$index] = "\"{$keyword}\"";
|
||||||
|
}
|
||||||
|
$htmlEntityKeywords = implode(' AND ', $htmlEntityKeywordList);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
//We can get a list of all the tsvector columns though this query:
|
||||||
|
$result=DB::query("SELECT table_name, column_name, data_type FROM information_schema.columns WHERE data_type='tsvector';");
|
||||||
|
|
||||||
|
if (!$result->numRecords()) throw Exception('there are no full text columns to search');
|
||||||
|
|
||||||
|
$tables=Array();
|
||||||
|
|
||||||
|
foreach($result as $row){
|
||||||
|
|
||||||
|
if(substr($row['table_name'], -5)!='_Live' && substr($row['table_name'], -9)!='_versions') {
|
||||||
|
$thisSql = "SELECT \"ID\", '{$row['table_name']}' AS ClassName, ts_rank(\"{$row['column_name']}\", q) FROM \"{$row['table_name']}\", to_tsquery('english', '$keywords') AS q WHERE \"{$row['column_name']}\" @@@ q ";
|
||||||
|
$tables[] = $thisSql;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$doSet=new DataObjectSet();
|
||||||
|
|
||||||
|
$sortBy='ts_rank';
|
||||||
|
$limit=10;
|
||||||
|
$fullQuery = "SELECT * FROM (" . implode(" UNION ", $tables) . ") AS q1 ORDER BY $sortBy LIMIT $limit";
|
||||||
|
$totalCount=1;
|
||||||
|
// Get records
|
||||||
|
$records = DB::query($fullQuery);
|
||||||
|
|
||||||
|
foreach($records as $record){
|
||||||
|
$item=DB::query("SELECT * FROM \"{$record['classname']}\" WHERE \"ID\"={$record['ID']};")->first();
|
||||||
|
$objects[] = new $record['classname']($item);
|
||||||
|
}
|
||||||
|
if(isset($objects)) $doSet = new DataObjectSet($objects);
|
||||||
|
else $doSet = new DataObjectSet();
|
||||||
|
|
||||||
|
$doSet->setPageLimits($start, $pageLength, $totalCount);
|
||||||
|
|
||||||
|
|
||||||
|
//$resultRows=Array();
|
||||||
|
//Right, now we go and run each of these queries and take their rank and put them into an array accordinaly
|
||||||
|
//$totalCount=0;
|
||||||
|
//foreach($tables as $sql){
|
||||||
|
// $result=DB::query($sql);
|
||||||
|
// foreach($result as $row){
|
||||||
|
//
|
||||||
|
// $resultRows[$row['ts_rank']][]=Array('ID'=>$row['ID'], 'Table'=>$row['source']);
|
||||||
|
// $totalCount++;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//Now we populate the dataobject with the results in order of relevance:
|
||||||
|
/*foreach($resultRows as $row_array){
|
||||||
|
foreach($row_array as $row){
|
||||||
|
$item=DataObject::get_by_id($row['Table'], $row['ID']);
|
||||||
|
|
||||||
|
$searchResults->push($item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*$searchResults->setPageLimits($start, $pageLength, $totalCount);*/
|
||||||
|
|
||||||
|
return $doSet;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -961,17 +1199,14 @@ class PostgreSQLQuery extends Query {
|
|||||||
public function __construct(PostgreSQLDatabase $database, $handle) {
|
public function __construct(PostgreSQLDatabase $database, $handle) {
|
||||||
$this->database = $database;
|
$this->database = $database;
|
||||||
$this->handle = $handle;
|
$this->handle = $handle;
|
||||||
parent::__construct();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function __destroy() {
|
public function __destroy() {
|
||||||
//mysql_free_result($this->handle);
|
pg_free_result($this->handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function seek($row) {
|
public function seek($row) {
|
||||||
//return mysql_data_seek($this->handle, $row);
|
return pg_result_seek($this-handle, $row);
|
||||||
//This is unnecessary in postgres. You can just provide a row number with the fetch
|
|
||||||
//command.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function numRecords() {
|
public function numRecords() {
|
||||||
|
Loading…
Reference in New Issue
Block a user