diff --git a/.upgrade.yml b/.upgrade.yml new file mode 100644 index 0000000..1208f25 --- /dev/null +++ b/.upgrade.yml @@ -0,0 +1,7 @@ +mappings: + PostgreSQLConnector: SilverStripe\PostgreSQL\PostgreSQLConnector + PostgreSQLDatabase: SilverStripe\PostgreSQL\PostgreSQLDatabase + PostgreSQLDatabaseConfigurationHelper: SilverStripe\PostgreSQL\PostgreSQLDatabaseConfigurationHelper + PostgreSQLQuery: SilverStripe\PostgreSQL\PostgreSQLQuery + PostgreSQLQueryBuilder: SilverStripe\PostgreSQL\PostgreSQLQueryBuilder + PostgreSQLSchemaManager: SilverStripe\PostgreSQL\PostgreSQLSchemaManager diff --git a/_config.php b/_config.php deleted file mode 100644 index b3d9bbc..0000000 --- a/_config.php +++ /dev/null @@ -1 +0,0 @@ -dbConn) { pg_close($this->dbConn); } - + // Connect $this->dbConn = @pg_connect(implode(' ', $arguments)); if ($this->dbConn === false) { @@ -143,13 +148,13 @@ class PostgreSQLConnector extends DBConnector /** * Determines if the SQL fragment either breaks into or out of a string literal * by counting single quotes - * + * * Handles double-quote escaped quotes as well as slash escaped quotes - * + * * @todo Test this! - * + * * @see http://www.postgresql.org/docs/8.3/interactive/sql-syntax-lexical.html#SQL-SYNTAX-STRINGS - * + * * @param string $input The SQL fragment * @return boolean True if the string breaks into or out of a string literal */ @@ -167,9 +172,9 @@ class PostgreSQLConnector extends DBConnector /** * Iteratively replaces all question marks with numerical placeholders * E.g. "Title = ? AND Name = ?" becomes "Title = $1 AND Name = $2" - * + * * @todo Better consider question marks in string literals - * + * * @param string $sql Paramaterised query using question mark placeholders * @return string Paramaterised query using numeric placeholders */ @@ -221,13 +226,13 @@ class PostgreSQLConnector extends DBConnector } else { $result = pg_query($this->dbConn, $sql); } - + // Handle error if ($result === false) { $this->databaseError($this->getLastError(), $errorLevel, $sql, $parameters); return null; } - + // Save and return results $this->lastQuery = $result; $this->lastRows = pg_affected_rows($result); @@ -253,16 +258,6 @@ class PostgreSQLConnector extends DBConnector return pg_escape_string($this->dbConn, $value); } - public function escapeIdentifier($value, $separator = '.') - { - if (empty($separator) && function_exists('pg_escape_identifier')) { - return pg_escape_identifier($this->dbConn, $value); - } - - // Let parent function handle recursive calls - return parent::escapeIdentifier($value, $separator); - } - public function selectDatabase($name) { if ($name !== $this->databaseName) { diff --git a/code/PostgreSQLDatabase.php b/code/PostgreSQLDatabase.php index 00a7cbe..dd7657e 100644 --- a/code/PostgreSQLDatabase.php +++ b/code/PostgreSQLDatabase.php @@ -1,8 +1,19 @@ get('PostgreSQLDatabase', 'default_fts_cluster_method'); + return Config::inst()->get('SilverStripe\\PostgreSQL\\PostgreSQLDatabase', 'default_fts_cluster_method'); } /** * Full text search method. - * + * * @return string */ public static function default_fts_search_method() { - return Config::inst()->get('PostgreSQLDatabase', 'default_fts_search_method'); + return Config::inst()->get('SilverStripe\\PostgreSQL\\PostgreSQLDatabase', 'default_fts_search_method'); } /** @@ -55,13 +66,13 @@ class PostgreSQLDatabase extends SS_Database * Some locked down systems prevent access to the 'postgres' table in * which case you need to set this to false. * - * If allow_query_master_postgres is false, and model_schema_as_database is also false, + * If allow_query_master_postgres is false, and model_schema_as_database is also false, * then attempts to create or check databases beyond the initial connection will * result in a runtime error. */ public static function allow_query_master_postgres() { - return Config::inst()->get('PostgreSQLDatabase', 'allow_query_master_postgres'); + return Config::inst()->get('SilverStripe\\PostgreSQL\\PostgreSQLDatabase', 'allow_query_master_postgres'); } /** @@ -70,13 +81,13 @@ class PostgreSQLDatabase extends SS_Database * instead of using databases. This may be useful if the database user does not * have cross-database permissions, and in cases where multiple databases are used * (such as in running test cases). - * + * * If this is true then the database will only be set during the initial connection, * and attempts to change to this database will use the 'public' schema instead */ public static function model_schema_as_database() { - return Config::inst()->get('PostgreSQLDatabase', 'model_schema_as_database'); + return Config::inst()->get('SilverStripe\\PostgreSQL\\PostgreSQLDatabase', 'model_schema_as_database'); } /** @@ -84,16 +95,16 @@ class PostgreSQLDatabase extends SS_Database * could be any of the supported languages that can be found in the * pg_catalog.pg_ts_config table. * - * @var string + * @return string */ public static function search_language() { - return Config::inst()->get('PostgreSQLDatabase', 'search_language'); + return Config::inst()->get('SilverStripe\\PostgreSQL\\PostgreSQLDatabase', 'search_language'); } /** * The database name specified at initial connection - * + * * @var string */ protected $databaseOriginal = ''; @@ -183,7 +194,7 @@ class PostgreSQLDatabase extends SS_Database /** * Sets the system timezone for the database connection - * + * * @param string $timezone */ public function selectTimezone($timezone) @@ -211,7 +222,7 @@ class PostgreSQLDatabase extends SS_Database /** * Returns the name of the current schema in use - * + * * @return string Name of current schema */ public function currentSchema() @@ -222,8 +233,8 @@ class PostgreSQLDatabase extends SS_Database /** * Utility method to manually set the schema to an alternative * Check existance & sets search path to the supplied schema name - * - * @param string $name Name of the schema + * + * @param string $schema Name of the schema * @param boolean $create Flag indicating whether the schema should be created * if it doesn't exist. If $create is false and the schema doesn't exist * then an error will be raised @@ -256,14 +267,12 @@ class PostgreSQLDatabase extends SS_Database * the search path is provided as an advanced PostgreSQL feature for raw * SQL queries. Sapphire cannot search for datamodel tables in alternate * schemas, so be wary of using alternate schemas within the ORM environment. - * - * @param string $arg1 First schema to use - * @param string $arg2 Second schema to use - * @param string $argN Nth schema to use + * + * @param string ...$arg Schema name to use. Add additional schema names as extra arguments. */ - public function setSchemaSearchPath() + public function setSchemaSearchPath($arg = null) { - if (func_num_args() == 0) { + if (!$arg) { user_error('At least one Schema must be supplied to set a search path.', E_USER_ERROR); } $schemas = array_values(func_get_args()); @@ -274,8 +283,17 @@ class PostgreSQLDatabase extends SS_Database * The core search engine configuration. * @todo Properly extract the search functions out of the core. * + * @param array $classesToSearch * @param string $keywords Keywords as a space separated string - * @return object DataObjectSet of result pages + * @param int $start + * @param int $pageLength + * @param string $sortBy + * @param string $extraFilter + * @param bool $booleanSearch + * @param string $alternativeFileFilter + * @param bool $invertedMatch + * @return PaginatedList List of result pages + * @throws Exception */ public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "ts_rank DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) { @@ -580,7 +598,7 @@ class PostgreSQLDatabase extends SS_Database * Determines the name of the current database to be reported externally * by substituting the schema name for the database name. * Should only be used when model_schema_as_database is true - * + * * @param string $schema Name of the schema * @return string Name of the database to report */ @@ -595,7 +613,7 @@ class PostgreSQLDatabase extends SS_Database /** * Translates a requested database name to a schema name to substitute internally. * Should only be used when model_schema_as_database is true - * + * * @param string $database Name of the database * @return string Name of the schema to use for this database internally */ @@ -613,7 +631,7 @@ class PostgreSQLDatabase extends SS_Database // Check current schema is valid $oldSchema = $this->schema; if (empty($oldSchema)) { - return true; + return; } // Nothing selected to drop // Select another schema @@ -667,6 +685,7 @@ class PostgreSQLDatabase extends SS_Database // New connection made here, treating the new database name as the new original $this->databaseOriginal = $name; $this->connectDefault(); + return true; } /** diff --git a/code/PostgreSQLDatabaseConfigurationHelper.php b/code/PostgreSQLDatabaseConfigurationHelper.php index cf7ba7c..6e5ec9b 100644 --- a/code/PostgreSQLDatabaseConfigurationHelper.php +++ b/code/PostgreSQLDatabaseConfigurationHelper.php @@ -1,17 +1,25 @@ true, 'error' => 'details of error') */ @@ -119,28 +127,10 @@ class PostgreSQLDatabaseConfigurationHelper implements DatabaseConfigurationHelp 'error' => $error ); } - - /** - * Helper function to quote a string value - * - * @param mixed $conn Connection object/resource - * @param string $value Value to quote - * @return string Quoted strieng - */ - protected function quote($conn, $value) - { - if ($conn instanceof PDO) { - return $conn->quote($value); - } elseif (is_resource($conn)) { - return "'".pg_escape_string($conn, $value)."'"; - } else { - user_error('Invalid database connection', E_USER_ERROR); - } - } - + /** * Helper function to execute a query - * + * * @param mixed $conn Connection object/resource * @param string $sql SQL string to execute * @return array List of first value from each resulting row @@ -178,7 +168,7 @@ class PostgreSQLDatabaseConfigurationHelper implements DatabaseConfigurationHelp $success = in_array($databaseConfig['username'], $allowedUsers); } } - + return array( 'success' => $success, 'alreadyExists' => $alreadyExists diff --git a/code/PostgreSQLQuery.php b/code/PostgreSQLQuery.php index 7b1fc7e..811c610 100644 --- a/code/PostgreSQLQuery.php +++ b/code/PostgreSQLQuery.php @@ -1,8 +1,12 @@ database->databaseToSchemaName($name); - return $this->dropSchema($schemaName); + $this->dropSchema($schemaName); + return; } $this->dropPostgresDatabase($name); } /** * Returns true if the schema exists in the current database - * + * * @param string $name * @return boolean */ @@ -143,7 +150,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * Creates a schema in the current database - * + * * @param string $name */ public function createSchema($name) @@ -154,7 +161,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * Drops a schema from the database. Use carefully! - * + * * @param string $name */ public function dropSchema($name) @@ -165,7 +172,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * Returns the list of all available schemas on the current database - * + * * @return array */ public function schemaList() @@ -257,9 +264,9 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * Builds the internal Postgres index name given the silverstripe table and index name - * + * * @param string $tableName - * @param string $indexName + * @param string $indexName * @param string $prefix The optional prefix for the index. Defaults to "ix" for indexes. * @return string The postgres name of the index */ @@ -281,7 +288,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * Builds the internal Postgres trigger name given the silverstripe table and trigger name - * + * * @param string $tableName * @param string $triggerName * @return string The postgres name of the trigger @@ -400,7 +407,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager if ($alteredOptions && isset($this->class) && isset($alteredOptions[$this->class])) { $this->query(sprintf("ALTER TABLE \"%s\" %s", $table, $alteredOptions[$this->class])); - Database::alteration_message( + DB::alteration_message( sprintf("Table %s options changed: %s", $table, $alteredOptions[$this->class]), "changed" ); @@ -563,7 +570,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * 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 @@ -697,7 +704,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * 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. @@ -773,7 +780,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager //create a type-specific index // NOTE: hash should be removed. This is only here to demonstrate how other indexes can be made - // NOTE: Quote the index name to preserve case sensitivity + // NOTE: Quote the index name to preserve case sensitivity switch ($indexSpec['type']) { case 'fulltext': // @see fulltext() for the definition of the trigger that ts_$IndexName uses for fulltext searching @@ -808,6 +815,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager if ($indexSpec[0] != '(') { list($indexType, $indexFields) = explode(' ', $indexSpec, 2); } else { + $indexType = null; $indexFields = $indexSpec; } @@ -961,7 +969,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /** * A function to return the field names and datatypes for the particular table - * + * * @param string $tableName * @return array List of columns an an associative array with the keys Column and DataType */ @@ -1045,28 +1053,12 @@ class PostgreSQLSchemaManager extends DBSchemaManager * Return a boolean type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function boolean($values, $asDbValue=false) + public function boolean($values) { - //Annoyingly, we need to do a good ol' fashioned switch here: $default = $values['default'] ? '1' : '0'; - - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($asDbValue) { - return array('data_type'=>'smallint'); - } - - if ($values['arrayValue'] != '') { - $default = ''; - } else { - $default = ' default ' . (int)$values['default']; - } - return "smallint{$values['arrayValue']}" . $default; + return "smallint default {$default}"; } /** @@ -1077,26 +1069,17 @@ class PostgreSQLSchemaManager extends DBSchemaManager */ public function date($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - return "date{$values['arrayValue']}"; + return "date"; } /** * Return a decimal type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function decimal($values, $asDbValue=false) + public function decimal($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - // Avoid empty strings being put in the db if ($values['precision'] == '') { $precision = 1; @@ -1109,11 +1092,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager $defaultValue = ' default ' . floatval($values['default']); } - if ($asDbValue) { - return array('data_type' => 'numeric', 'precision' => $precision); - } else { - return "decimal($precision){$values['arrayValue']}$defaultValue"; - } + return "decimal($precision)$defaultValue"; } /** @@ -1124,103 +1103,52 @@ class PostgreSQLSchemaManager extends DBSchemaManager */ 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 - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($values['arrayValue']!='') { - $default = ''; - } else { - $default = " default '{$values['default']}'"; - } - - return "varchar(255){$values['arrayValue']}" . $default . " check (\"" . $values['name'] . "\" in ('" . implode('\', \'', $values['enums']) . "'))"; + $default = " default '{$values['default']}'"; + return "varchar(255)" . $default . " check (\"" . $values['name'] . "\" in ('" . implode('\', \'', $values['enums']) . "'))"; } /** * Return a float type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function float($values, $asDbValue = false) + public function float($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($asDbValue) { - return array('data_type' => 'double precision'); - } else { - return "float{$values['arrayValue']}"; - } + return "float"; } /** * Return a float type-formatted string cause double is not supported * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function double($values, $asDbValue=false) + public function double($values) { - return $this->float($values, $asDbValue); + return $this->float($values); } /** * Return a int type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function int($values, $asDbValue = false) + public function int($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($asDbValue) { - return array('data_type'=>'integer', 'precision'=>'32'); - } - - if ($values['arrayValue']!='') { - $default=''; - } else { - $default=' default ' . (int)$values['default']; - } - - return "integer{$values['arrayValue']}" . $default; + return "integer default " . (int)$values['default']; } /** * Return a bigint type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function bigint($values, $asDbValue = false) + public function bigint($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($asDbValue) { - return array('data_type'=>'bigint', 'precision'=>'64'); - } - - if ($values['arrayValue']!='') { - $default=''; - } else { - $default=' default ' . (int)$values['default']; - } - - return "bigint{$values['arrayValue']}" . $default; + return "bigint default" . (int)$values['default']; } /** @@ -1228,40 +1156,22 @@ class PostgreSQLSchemaManager extends DBSchemaManager * For PostgreSQL, we simply return the word 'timestamp', no other parameters are necessary * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function SS_Datetime($values, $asDbValue = false) + public function datetime($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue']=''; - } - - if ($asDbValue) { - return array('data_type'=>'timestamp without time zone'); - } else { - return "timestamp{$values['arrayValue']}"; - } + return "timestamp"; } /** * Return a text type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function text($values, $asDbValue = false) + public function text($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue'] = ''; - } - - if ($asDbValue) { - return array('data_type'=>'text'); - } else { - return "text{$values['arrayValue']}"; - } + return "text"; } /** @@ -1272,57 +1182,34 @@ class PostgreSQLSchemaManager extends DBSchemaManager */ public function time($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue'] = ''; - } - - return "time{$values['arrayValue']}"; + return "time"; } /** * Return a varchar type-formatted string * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function varchar($values, $asDbValue=false) + public function varchar($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue'] = ''; - } - if (!isset($values['precision'])) { $values['precision'] = 255; } - if ($asDbValue) { - return array('data_type'=>'varchar', 'precision'=>$values['precision']); - } else { - return "varchar({$values['precision']}){$values['arrayValue']}"; - } + return "varchar({$values['precision']})"; } /* * Return a 4 digit numeric type. MySQL has a proprietary 'Year' type. * For Postgres, we'll use a 4 digit numeric - * + * * @param array $values Contains a tokenised list of info about this data type - * @param boolean $asDbValue * @return string */ - public function year($values, $asDbValue = false) + public function year($values) { - if (!isset($values['arrayValue'])) { - $values['arrayValue'] = ''; - } - - //TODO: the DbValue result does not include the numeric_scale option (ie, the ,0 value in 4,0) - if ($asDbValue) { - return array('data_type'=>'decimal', 'precision'=>'4'); - } else { - return "decimal(4,0){$values['arrayValue']}"; - } + return "decimal(4,0)"; } /** @@ -1334,7 +1221,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager * @param array $this_index Index specification for the fulltext index * @param string $tableName * @param string $name - * @param array $spec + * @return array */ protected function fulltext($this_index, $tableName, $name) { @@ -1431,7 +1318,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /* * Given a tablespace and and location, either create a new one * or update the existing one - * + * * @param string $name * @param string $location */ @@ -1456,13 +1343,13 @@ class PostgreSQLSchemaManager extends DBSchemaManager } /** - * + * * @param string $tableName * @param array $partitions * @param array $indexes * @param array $extensions */ - public function createOrReplacePartition($tableName, $partitions, $indexes, $extensions) + public function createOrReplacePartition($tableName, $partitions, $indexes = [], $extensions = []) { //We need the plpgsql language to be installed for this to work: @@ -1550,7 +1437,7 @@ class PostgreSQLSchemaManager extends DBSchemaManager /* * This will create a language if it doesn't already exist. * This is used by the createOrReplacePartition function, which needs plpgsql - * + * * @param string $language Language name */ public function createLanguage($language) diff --git a/tests/PostgreSQLConnectorTest.php b/tests/PostgreSQLConnectorTest.php index 6dba478..82653e5 100644 --- a/tests/PostgreSQLConnectorTest.php +++ b/tests/PostgreSQLConnectorTest.php @@ -1,5 +1,7 @@