Compare commits

..

73 Commits
2.0.0 ... 2

Author SHA1 Message Date
Steve Boyd
091a4c2416 Merge branch '2.3' into 2 2024-07-11 16:21:38 +12:00
Guy Sartorelli
b37c19d6d5
Merge pull request #152 from creative-commoners/pulls/2.3/composer
DEP Bump minimum version of framework
2024-07-11 10:00:11 +12:00
Steve Boyd
24dbdeb564 DEP Bump minimum version of framework 2024-07-10 12:31:34 +12:00
Maxime Rainville
a60fc4cf24
Merge pull request #146 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:15:32 +13:00
Steve Boyd
d2fbce5319 MNT Use gha-dispatch-ci 2023-03-21 14:32:24 +13:00
Steve Boyd
beb0f84f2d Merge branch '2.3' into 2 2022-08-03 14:33:11 +12:00
Guy Sartorelli
6d5c35116a
Merge pull request #132 from creative-commoners/pulls/2.3/standardise-modules
MNT Standardise modules
2022-08-03 10:09:54 +12:00
Steve Boyd
41fd4718a2 MNT Standardise modules 2022-08-02 17:55:18 +12:00
Steve Boyd
448828c20a Merge branch '2.3' into 2 2022-07-26 16:45:04 +12:00
Guy Sartorelli
faf9d033ff
Merge pull request #130 from creative-commoners/pulls/2.3/module-standards
MNT Use GitHub Actions CI
2022-07-15 12:45:58 +12:00
Steve Boyd
d7b4ccb202 MNT Use GitHub Actions CI 2022-07-15 12:23:59 +12:00
Guy Sartorelli
c9bce8fe57 Merge branch '2.3' into 2 2022-06-14 11:31:28 +12:00
Sabina Talipova
4bbabf2421
Merge pull request #128 from creative-commoners/pulls/2.3/phpunit9
Approved
2022-06-07 08:00:53 +12:00
Steve Boyd
22e3951244 MNT Test using PHPUnit9 2022-06-02 12:21:46 +12:00
Steve Boyd
321d0d853b Merge branch '2.3' into 2 2022-04-22 09:32:30 +12:00
Guy Sartorelli
84e13ffde3
Merge pull request #127 from creative-commoners/pulls/2.3/chars
FIX Ignore invalid byte character
2022-04-21 18:40:22 +12:00
Steve Boyd
17e9f5388c FIX Ignore invalid byte character 2022-04-21 14:14:28 +12:00
Steve Boyd
222f20529c
Update build status badge 2021-01-21 16:37:01 +13:00
Steve Boyd
c46272e751 Merge branch '2.3' into 2 2021-01-18 13:59:53 +13:00
Steve Boyd
afec73997a
Merge pull request #121 from creative-commoners/pulls/2.3/travis-matrix
MNT Rearrange travis matrix
2021-01-18 13:58:22 +13:00
Steve Boyd
d3c8e2915e MNT Rearrange travis matrix 2021-01-18 11:37:36 +13:00
Steve Boyd
a709a741b0 Merge branch '2.3' into 2 2021-01-02 20:09:58 +13:00
Maxime Rainville
dd4df9800b
Merge pull request #119 from creative-commoners/pulls/2.3/travis-shared
MNT Travis shared config, use sminnee/phpunit
2020-12-21 15:03:34 +13:00
Steve Boyd
082742ad23 MNT Travis shared config, use sminnee/phpunit 2020-12-02 15:41:59 +13:00
Robbie Averill
a7c3450d43 Merge branch 'pulls/docs-known-issue' into 2
# Conflicts:
 #	README.md
 #
2019-11-27 17:33:06 -08:00
Robbie Averill
0be39423a3 Merge branch '2.3' into 2 2019-11-27 17:28:00 -08:00
Robbie Averill
3d50b3f9ec Merge branch '2.2' into 2.3 2019-11-27 17:27:36 -08:00
Maxime Rainville
3e38f845e3 Bump branch alias to 2.4. 2019-10-02 14:48:55 +13:00
Maxime Rainville
4e3d3df565 Remove branch alias for 2.3.0 release 2019-10-02 14:45:33 +13:00
Maxime Rainville
753d73e1fe
BUG Fix issues preventing a site from being migrated from SS3 to SS4 (#104)
* BUG Enum value change wasn't being detected by alterTableAlterColumn because backslashes were not accounting

* BUG Update renameTable to also rename constraints

* BUG Add unit tests to cover requireTable and renameTable

* Fix liniting errors

* MINOR Update build to use xenial and add extra PHP version
2019-09-26 10:25:41 +12:00
Guy Marriott
beed6c7fb7
API Increased support to 9.2 (#102)
API Increased support to 9.2
2019-09-17 16:17:43 -07:00
Ingo Schommer
bf4fb87a01 API Increased support to 9.2
Anything older than 9.3 is unsupported by Postgres: https://www.postgresql.org/support/versioning/.
I don't think we should claim support in our module for unsupported versions,
particularly if we don't have CI on them.

Since we're *only* running CI on 9.2 at the moment, that's the safe claim
for our module support, even though it's already unsupported by Postgres.
See https://docs.travis-ci.com/user/database-setup/#using-a-different-postgresql-version

I'll raise a separate ticket about testing and supporting newer versions,
it's out of scope for this PR.
2019-09-03 15:41:23 +12:00
Ingo Schommer
66376db094 DOCS Known issue about collations
Moving from https://docs.silverstripe.org/en/4/getting_started/server_requirements/
since it's too much noise there.

Also removing the maintainer contact, that's an outdated concept.
2019-09-03 15:22:14 +12:00
Ingo Schommer
d607a2bfa9
Merge pull request #100 from sminnee/drop-php5
NEW: Drop PHP 5.6 testing
2019-07-04 08:59:40 +12:00
Sam Minnée
f2ec228c72
Merge pull request #99 from sminnee/fix-9097
FIX: Don’t drop first row on repeated iteration
2019-07-03 14:27:21 +12:00
Sam Minnee
82f8a06afa NEW: Drop PHP 5.6 testing
PostgreSQL 2.3+, like Framework 4.5+, is able to be PHP 7.1+. Updated
the test matrix to reflect this.
2019-07-03 13:58:50 +12:00
Sam Minnee
75f4a35f71 FIX: Don’t drop first row on repeated iteration
Fixes https://github.com/silverstripe/silverstripe-framework/issues/9097

Related: https://github.com/silverstripe/silverstripe-framework/issues/9098

Co-authored-by: Guy Marriott <guy@scopey.co.nz>
2019-07-03 13:55:17 +12:00
Guy Marriott
08c8293328
Merge pull request #97 from open-sausages/pulls/2/fix-aliasing
FIX Usage of a bug-feature around aliases
2019-05-09 12:38:33 +12:00
Serge Latyntcev
0ffaf90512 FIX Usage of a bug-feature around aliases
Related https://github.com/silverstripe/silverstripe-postgresql/issues/95
2019-05-06 16:06:58 +12:00
Guy Marriott
04000ad878
Merge remote-tracking branch 'origin/2.2' into 2 2019-04-15 15:36:11 +12:00
Guy Marriott
3d6920c121
Remove branch alias 2019-04-15 15:14:05 +12:00
Guy Marriott
b6bab3561f
Bump branch alias for 2.3.x-dev 2019-04-15 11:11:50 +12:00
Sam Minnee
fd27c17a80 MINOR: Add comment to explain ‘f’ coercion.
Follow-up to https://github.com/silverstripe/silverstripe-postgresql/pull/93
2019-01-23 22:19:26 +13:00
Robbie Averill
f85b46d047
Merge pull request #93 from sminnee/fix-boolean-coersion
FIX: Boolean ’t’/‘f’ strings need to be coerced to int properly.
2019-01-23 11:16:53 +02:00
Sam Minnee
32a0aad720 FIX: Boolean ’t’/‘f’ strings need to be coerced to int properly. 2019-01-23 13:50:32 +13:00
Loz Calver
0d9fcabc80
Merge pull request #91 from sminnee/strict-types
Strict types
2018-11-05 10:14:23 +01:00
Sam Minnee
8f70ac89ca FIX: Removed test that has been moved back to framework
This test has been added for all database types in framework
in https://github.com/silverstripe/silverstripe-framework/pull/8448
2018-11-05 18:13:47 +13:00
Sam Minnee
72787ae83e FIX: Return correct types in PostgreSQLQuery
Fixes https://github.com/silverstripe/silverstripe-postgresql/issues/90
Helps fix https://github.com/silverstripe/silverstripe-framework/issues/7039
2018-11-05 18:13:47 +13:00
Robbie Averill
4c6034f350 Bump branch alias for 2.2.x-dev 2018-09-24 12:49:53 +02:00
Robbie Averill
edfa209a3c Merge branch '2.1' into 2 2018-09-24 12:49:36 +02:00
Robbie Averill
e123f69b7b Remove obsolete branch alias 2018-09-24 12:48:49 +02:00
Daniel Hensby
4c89d103c5
Merge branch '1' into 2 2018-07-13 17:42:10 +01:00
Maxime Rainville
6378003540 Prefer source so we get the tests. 2018-07-02 11:47:15 +12:00
Maxime Rainville
b210c7284f Targeting 4.3.x-dev
Third time's a charm.
2018-07-02 11:47:15 +12:00
Maxime Rainville
7fe935fc89 Correct typo in targeted version. 2018-07-02 11:47:15 +12:00
Maxime Rainville
694c4059b9 Test against recipe-cms 4. 2018-07-02 11:47:15 +12:00
Robbie Averill
6cfc30952c
Merge pull request #86 from open-sausages/pulls/2/report-transaction-nesting
API Support better transaction nesting
2018-06-19 16:57:46 +12:00
Damian Mooyman
e0d5536715
API Support better transaction nesting 2018-06-19 16:20:07 +12:00
Damian Mooyman
01cc78ec94
Merge pull request #85 from creative-commoners/pulls/master/add-supported-module-badge
Add supported module badge to readme
2018-06-18 10:12:41 +12:00
Dylan Wagstaff
513c969c93 Add supported module badge to readme 2018-06-15 17:43:43 +12:00
Damian Mooyman
e3825697d0
Merge remote-tracking branch 'origin/2.0' into 2 2018-02-09 10:05:32 +13:00
Daniel Hensby
47a6ebb4e3 FIX Allow nested transactions 2018-02-09 10:05:00 +13:00
Damian Mooyman
2bbd73620d
Merge pull request #83 from dhensby/pulls/2.0/nested-transactions
FIX Allow nested transactions
2018-02-09 09:50:46 +13:00
Daniel Hensby
97afbd9a88
FIX Allow nested transactions 2018-02-08 20:00:49 +00:00
Damian Mooyman
f2392eb7c6
Merge remote-tracking branch 'origin/2.0' into 2 2017-12-07 16:31:21 +13:00
Damian Mooyman
1f6d892609
Merge remote-tracking branch 'origin/1.2' into 2.0
# Conflicts:
#	code/PostgreSQLDatabase.php
2017-12-07 16:30:22 +13:00
Damian Mooyman
2d11336dce
Merge remote-tracking branch 'origin/1.2' into 1 2017-12-07 16:26:06 +13:00
Damian Mooyman
aa16771922
Merge pull request #1 from silverstripe-security/patch/1.2/SS-2017-008
[SS-2017-008] Fix SQL injection in search engine
2017-12-07 15:59:04 +13:00
Damian Mooyman
05e15d85d6
Merge branch '2.0' into 2 2017-11-28 10:37:16 +13:00
Loz Calver
390cb09928
FIX: Add missing $totalCount variable 2017-11-25 21:40:01 +00:00
Daniel Hensby
d110b92fc8
Merge pull request #80 from kinglozzer/fix-searchengine
Fix PostgreSQLDatabase::searchEngine()
2017-11-25 18:09:58 +00:00
Loz Calver
851309f187 Fix PostgreSQLDatabase::searchEngine() 2017-11-24 22:28:01 +00:00
Daniel Hensby
ee356b1ad7
[SS-2017-008] Fix SQL injection in search engine 2017-11-21 15:34:59 +00:00
15 changed files with 394 additions and 254 deletions

25
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
with:
# set phpunit to false to prevent automatic generation of mysql phpunit jobs
phpunit: false
extra_jobs: |
- php: 7.4
db: pgsql
phpunit: true
composer_args: --prefer-lowest
- php: 8.0
db: pgsql
phpunit: true
- php: 8.1
db: pgsql
phpunit: true

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 12:20 PM UTC, only on Thursday and Friday
schedule:
- cron: '20 12 * * 4,5'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

View File

@ -1,15 +0,0 @@
inherit: true
build:
nodes:
analysis:
tests:
override: [php-scrutinizer-run]
checks:
php:
code_rating: true
duplication: true
filter:
paths: [code/*, tests/*]

View File

@ -1,43 +0,0 @@
language: php
dist: trusty
cache:
directories:
- $HOME/.composer/cache/files
php:
- 5.6
env:
global:
- DB="PGSQL"
matrix:
fast_finish: true
include:
- php: 5.6
env:
- PHPUNIT_TEST=framework
- php: 5.6
env:
- PHPUNIT_TEST=postgresql
- PHPCS_TEST=1
before_script:
# Init PHP
- phpenv rehash
- phpenv config-rm xdebug.ini
- export PATH=~/.composer/vendor/bin:$PATH
- echo 'memory_limit = 2048M' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
# Install composer dependencies
- composer validate
- composer require --no-update silverstripe/recipe-cms:1.0.x-dev
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
- if [[ $PHPCS_TEST ]]; then composer global require squizlabs/php_codesniffer:^3 --prefer-dist --no-interaction --no-progress --no-suggest -o; fi
script:
- if [[ $PHPUNIT_TEST == postgresql ]]; then vendor/bin/phpunit ./tests; fi
- if [[ $PHPUNIT_TEST == framework ]]; then vendor/bin/phpunit ./vendor/silverstripe/framework/tests/php; fi
- if [[ $PHPCS_TEST ]]; then composer run-script lint; fi

View File

@ -1,34 +1,54 @@
# PostgreSQL Module Module
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-postgresql.png?branch=master)](https://travis-ci.org/silverstripe/silverstripe-postgresql)
## Maintainer Contact
* Sam Minnee (Nickname: sminnee) <sam@silverstripe.com>
[![CI](https://github.com/silverstripe/silverstripe-postgresql/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-postgresql/actions/workflows/ci.yml)
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Requirements
* SilverStripe 4.0
* PostgreSQL 8.3.x or greater must be installed
* PostgreSQL <8.3.0 may work if T-Search is manually installed
* Known to work on OS X Leopard, Windows Server 2008 R2 and Linux
* Silverstripe 4.0
* PostgreSQL >=9.2
* Note: PostgreSQL 10 has not been tested
## Installation
1. Install via composer `composer require silverstripe/postgresql` or extract the contents
so they reside as a `postgresql` directory inside your SilverStripe project code
2. Open the installer by browsing to install.php, e.g. http://localhost/silverstripe/install.php
3. Select PostgreSQL in the database list and enter your database details
```
composer require silverstripe/postgresql
```
## Configuration
### Environment file
Add the following settings to your `.env` file:
```
SS_DATABASE_CLASS=PostgreSQLDatabase
SS_DATABASE_USERNAME=
SS_DATABASE_PASSWORD=
```
See [environment variables](https://docs.silverstripe.org/en/4/getting_started/environment_management) for more details. Note that a database will automatically be created via `dev/build`.
### Through the installer
Open the installer by browsing to install.php, e.g. http://localhost/install.php
Select PostgreSQL in the database list and enter your database details
## Usage Overview
See docs/en for more information about configuring the module.
See [docs/en](docs/en/README.md) for more information about configuring the module.
## Known issues
All column and table names must be double-quoted. PostgreSQL automatically
lower-cases columns, and your queries will fail if you don't.
Collations have known issues when installed on Alpine, MacOS X and BSD derivatives
(see [PostgreSQL FAQ](https://wiki.postgresql.org/wiki/FAQ#Why_do_my_strings_sort_incorrectly.3F)).
We do not support such installations, although they still may work correctly for you.
As a workaround for PostgreSQL 10+ you could manually switch to ICU collations (e.g. und-x-icu).
There are no known workarounds for PostgreSQL <10.
Ts_vector columns are not automatically detected by the built-in search
filters. That means if you're doing a search through the CMS on a ModelAdmin
object, it will use LIKE queries which are very slow. If you're writing your

View File

@ -36,6 +36,11 @@ class PostgreSQLDatabase extends Database
*/
protected $schema;
/**
* @var bool
*/
protected $transactionNesting = 0;
/**
* Toggle if transactions are supported. Defaults to true.
*
@ -362,6 +367,9 @@ class PostgreSQLDatabase extends Database
*/
public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "ts_rank DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false)
{
$start = (int)$start;
$pageLength = (int)$pageLength;
//Fix the keywords to be ts_query compatitble:
//Spaces must have pipes
//@TODO: properly handle boolean operators here.
@ -383,9 +391,9 @@ class PostgreSQLDatabase extends Database
$classesPlaceholders = DB::placeholders($classesToSearch);
$searchableColumns = $this->preparedQuery(
"
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE data_type='tsvector' AND table_name in ($classesPlaceholders);",
SELECT table_name, column_name, data_type
FROM information_schema.columns
WHERE data_type='tsvector' AND table_name in ($classesPlaceholders);",
array_values($tablesToSearch)
);
if (!$searchableColumns->numRecords()) {
@ -440,7 +448,7 @@ class PostgreSQLDatabase extends Database
// Could parameterise this, but convention is only to to so for where conditions
$query->addFrom(array(
'tsearch' => ", to_tsquery('" . self::search_language() . "', $keywords) AS q"
'q' => ", to_tsquery('" . self::search_language() . "', $keywords)"
));
$query->setSelect(array());
@ -516,15 +524,20 @@ class PostgreSQLDatabase extends Database
public function transactionStart($transaction_mode = false, $session_characteristics = false)
{
$this->query('BEGIN;');
if ($this->transactionNesting > 0) {
$this->transactionSavepoint('NESTEDTRANSACTION' . $this->transactionNesting);
} else {
$this->query('BEGIN;');
if ($transaction_mode) {
$this->query("SET TRANSACTION {$transaction_mode};");
}
if ($transaction_mode) {
$this->query("SET TRANSACTION {$transaction_mode};");
}
if ($session_characteristics) {
$this->query("SET SESSION CHARACTERISTICS AS TRANSACTION {$session_characteristics};");
if ($session_characteristics) {
$this->query("SET SESSION CHARACTERISTICS AS TRANSACTION {$session_characteristics};");
}
}
++$this->transactionNesting;
}
public function transactionSavepoint($savepoint)
@ -534,16 +547,40 @@ class PostgreSQLDatabase extends Database
public function transactionRollback($savepoint = false)
{
// Named savepoint
if ($savepoint) {
$this->query("ROLLBACK TO {$savepoint};");
} else {
$this->query('ROLLBACK;');
$this->query('ROLLBACK TO ' . $savepoint);
return true;
}
// Abort if unable to unnest, otherwise jump up a level
if (!$this->transactionNesting) {
return false;
}
--$this->transactionNesting;
// Rollback nested
if ($this->transactionNesting > 0) {
return $this->transactionRollback('NESTEDTRANSACTION' . $this->transactionNesting);
}
// Rollback top level
$this->query('ROLLBACK');
return true;
}
public function transactionDepth()
{
return $this->transactionNesting;
}
public function transactionEnd($chain = false)
{
$this->query('COMMIT;');
--$this->transactionNesting;
if ($this->transactionNesting <= 0) {
$this->transactionNesting = 0;
$this->query('COMMIT;');
}
}
public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null, $parameterised = false)
@ -583,7 +620,7 @@ class PostgreSQLDatabase extends Database
{
preg_match_all('/%(.)/', $format, $matches);
foreach ($matches[1] as $match) {
if (array_search($match, array('Y', 'm', 'd', 'H', 'i', 's', 'U')) === false) {
if (array_search($match, array('Y','m','d','H','i','s','U')) === false) {
user_error('formattedDatetimeClause(): unsupported format character %' . $match, E_USER_WARNING);
}
}

View File

@ -18,6 +18,23 @@ class PostgreSQLQuery extends Query
*/
private $handle;
private $columnNames = [];
/**
* Mapping of postgresql types to PHP types
* Note that the bool => int mapping is by design, designed to mimic MySQL's behaviour
* @var array
*/
protected static $typeMapping = [
'bool' => 'int',
'int2' => 'int',
'int4' => 'int',
'int8' => 'int',
'float4' => 'float',
'float8' => 'float',
'numeric' => 'float',
];
/**
* Hook the result-set given into a Query class, suitable for use by sapphire.
* @param resource $handle the internal Postgres handle that is points to the resultset.
@ -25,6 +42,11 @@ class PostgreSQLQuery extends Query
public function __construct($handle)
{
$this->handle = $handle;
$numColumns = pg_num_fields($handle);
for ($i = 0; $i<$numColumns; $i++) {
$this->columnNames[$i] = pg_field_name($handle, $i);
}
}
public function __destruct()
@ -36,8 +58,10 @@ class PostgreSQLQuery extends Query
public function seek($row)
{
pg_result_seek($this->handle, $row);
return pg_fetch_assoc($this->handle);
// Specifying the zero-th record here will reset the pointer
$result = pg_fetch_array($this->handle, $row, PGSQL_NUM);
return $this->parseResult($result);
}
public function numRecords()
@ -47,6 +71,39 @@ class PostgreSQLQuery extends Query
public function nextRecord()
{
return pg_fetch_assoc($this->handle);
$row = pg_fetch_array($this->handle, null, PGSQL_NUM);
// Correct non-string types
if ($row) {
return $this->parseResult($row);
}
return false;
}
/**
* @param array $row
* @return array
*/
protected function parseResult(array $row)
{
$record = [];
foreach ($row as $i => $v) {
$k = $this->columnNames[$i];
$record[$k] = $v;
$type = pg_field_type($this->handle, $i);
if (isset(self::$typeMapping[$type])) {
if ($type === 'bool' && $record[$k] === 't') {
$record[$k] = 1;
// Note that boolean 'f' will be converted to 0 by this
} else {
settype($record[$k], self::$typeMapping[$type]);
}
}
}
return $record;
}
}

View File

@ -494,9 +494,15 @@ class PostgreSQLSchemaManager extends DBSchemaManager
// First, we split the column specifications into parts
// TODO: this returns an empty array for the following string: int(11) not null auto_increment
// on second thoughts, why is an auto_increment field being passed through?
$pattern = '/^([\w(\,)]+)\s?((?:not\s)?null)?\s?(default\s[\w\.\']+)?\s?(check\s[\w()\'",\s]+)?$/i';
$pattern = '/^([\w(\,)]+)\s?((?:not\s)?null)?\s?(default\s[\w\.\'\\\\]+)?\s?(check\s[\w()\'",\s\\\\]+)?$/i';
preg_match($pattern, $colSpec, $matches);
// example value this regex is expected to parse:
// varchar(255) not null default 'SS\Test\Player' check ("ClassName" in ('SS\Test\Player', 'Player', null))
// split into:
// * varchar(255)
// * not null
// * default 'SS\Test\Player'
// * check ("ClassName" in ('SS\Test\Player', 'Player', null))
if (sizeof($matches) == 0) {
return '';
@ -537,8 +543,8 @@ class PostgreSQLSchemaManager extends DBSchemaManager
if ($this->hasTable("{$tableName}_Live")) {
$updateConstraint .= "UPDATE \"{$tableName}_Live\" SET \"$colName\"='$default' WHERE \"$colName\" NOT IN ($constraint_values);";
}
if ($this->hasTable("{$tableName}_versions")) {
$updateConstraint .= "UPDATE \"{$tableName}_versions\" SET \"$colName\"='$default' WHERE \"$colName\" NOT IN ($constraint_values);";
if ($this->hasTable("{$tableName}_Versions")) {
$updateConstraint .= "UPDATE \"{$tableName}_Versions\" SET \"$colName\"='$default' WHERE \"$colName\" NOT IN ($constraint_values);";
}
$this->query($updateConstraint);
@ -560,8 +566,17 @@ class PostgreSQLSchemaManager extends DBSchemaManager
public function renameTable($oldTableName, $newTableName)
{
$constraints = $this->getConstraintForTable($oldTableName);
$this->query("ALTER TABLE \"$oldTableName\" RENAME TO \"$newTableName\"");
if ($constraints) {
foreach ($constraints as $old) {
$new = preg_replace('/^' . $oldTableName . '/', $newTableName, $old);
$this->query("ALTER TABLE \"$newTableName\" RENAME CONSTRAINT \"$old\" TO \"$new\";");
}
}
unset(self::$cached_fieldlists[$oldTableName]);
unset(self::$cached_constraints[$oldTableName]);
}
public function checkAndRepairTable($tableName)
@ -843,7 +858,9 @@ class PostgreSQLSchemaManager extends DBSchemaManager
$argList = array();
$nextArg = "";
foreach ($bytes as $byte) {
if ($byte == "00") {
if ($byte == '\x') {
continue;
} elseif ($byte == "00") {
$argList[] = $nextArg;
$nextArg = "";
} else {
@ -961,6 +978,28 @@ class PostgreSQLSchemaManager extends DBSchemaManager
return self::$cached_constraints[$constraint];
}
/**
* Retrieve a list of constraints for the provided table name.
* @param string $tableName
* @return array
*/
private function getConstraintForTable($tableName)
{
// Note the PostgreSQL `like` operator is case sensitive
$constraints = $this->preparedQuery(
"
SELECT conname
FROM pg_catalog.pg_constraint r
INNER JOIN pg_catalog.pg_namespace n
ON r.connamespace = n.oid
WHERE r.contype = 'c' AND conname like ? AND n.nspname = ?
ORDER BY 1;",
array($tableName . '_%', $this->database->currentSchema())
)->column('conname');
return $constraints;
}
/**
* A function to return the field names and datatypes for the particular table
*

View File

@ -15,11 +15,12 @@
}
],
"require": {
"silverstripe/framework": "^4",
"silverstripe/framework": "^4.10",
"silverstripe/vendor-plugin": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3"
},
"autoload": {
"psr-4": {

View File

@ -1,86 +1,5 @@
# PostgreSQL Database Module
SilverStripe now has tentative support for PostgreSQL ('Postgres').
## Requirements
SilverStripe 2.4.0 or greater. (PostgreSQL support is NOT available
in 2.3.).
SilverStripe supports Postgres versions 8.3.x, 8.4.x and onwards.
Postgres 8.3.0 launched in February 2008, so SilverStripe has a fairly
modern but not bleeding edge Postgres version requirement.
Support for 8.2.x is theoretically possible if you're willing to manually
install T-search. 8.2.x has not been tested either, so there may be other
compatibility issues. The EnterpriseDB versions of Postgres also work, if
you'd prefer a tuned version.
## Installation
You have three options to install PostgreSQL support with SilverStripe.
### Option 1 - Installer
The first option is to use the installer. However, this is currently only
supported since SilverStripe 2.4.0 beta2 (or using the daily builds).
1. Set up SilverStripe somewhere where you can start the installer - you
should only see one database “MySQL” to install with.
2. Download a copy of the “postgresql” module from here:
http://silverstripe.org/postgresql-module
3. Extract the archive you downloaded. Rename the directory from
“postgresql-trunk-rxxxx” to “postgresql” and copy it into the SilverStripe
directory you just set up
4. Open the installer once again, and a new option “PostgreSQL” should appear.
You can now proceed through the installation without having to change any code.
### Option 2 - Manual
The second option is to setup PostgreSQL support manually. This can be achieved
by following these instructions:
1. Set up a fresh working copy of SilverStripe
2. Download a copy of the “postgresql” module from here: http://silverstripe.org/postgresql-module
3. Extract the archive you downloaded. Rename the directory from
“postgresql-trunk-rxxxx” to “postgresql” and copy it into the SilverStripe
directory you just set up.
4. Open up your mysite/_config.php file and add (or update) the $databaseConfig
array like so:
> $databaseConfig = array(
> 'type' => 'PostgreSQLDatabase',
> 'server' => '[server address e.g. localhost]',
> 'username' => 'postgres',
> 'password' => 'mypassword',
> 'database' => 'SS_mysite'
> );
Finally, visit dev/build so that SilverStripe can build the database schema and
default records.
### Option 3 - Environment file
Finally, the third option is to change your environment to point to
PostgreSQLDatabase as a database class. Do this if you're currently using an
_ss_environment.php file.
1. Download a copy of the “postgresql” module from here: http://silverstripe.org/postgresql-module
2. Extract the archive you downloaded. Rename the directory from
postgresql-trunk-rxxxx” to “postgresql” and copy it into your SS directory
3. Add the following to your existing _ss_environment.php file:
> define('SS_DATABASE_CLASS', 'PostgreSQLDatabase');
Last steps:
1. Ensure your SS_DATABASE_USERNAME and SS_DATABASE_PASSWORD defines in
_ss_environment.php are correct to the PostgreSQL server.
2. Ensure that your mysite/_config.php file has a database name defined, such
as “SS_mysite”.
3. Visit dev/build so that SilverStripe can build the database schema and
default records
## Features
Here is a quick list of what's different in the Postgres module (a full
@ -300,22 +219,4 @@ Otherwise this extension will try to connect to "postgres" Database to check DB
connection, no matter what you entered in the "Database Name" field during
installation.
Make sure you have set the "search_path" correct for your database user.
## Known Issues
All column and table names must be double-quoted. PostgreSQL automatically
lower-cases columns, and your queries will fail if you don't.
Ts_vector columns are not automatically detected by the built-in search filters.
That means if you're doing a search through the CMS on a ModelAdmin object, it
will use LIKE queries which are very slow.
If you're writing your own front-end search system, you can specify the columns
to use for search purposes, and you get the full benefits of T-Search.
If you are using unsupported modules, there may be instances of MySQL-specific
SQL queries which will need to be made database-agnostic where possible.
Make sure you have set the "search_path" correct for your database user.

View File

@ -2,6 +2,9 @@
<ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>code</file>
<file>tests</file>
<!-- base rules are PSR-2 -->
<rule ref="PSR2" >
<!-- Current exclusions -->

View File

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">

View File

@ -1,57 +0,0 @@
<?php
namespace SilverStripe\PostgreSQL\Tests;
use Exception;
use Page;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\PostgreSQL\PostgreSQLDatabase;
/**
* @package postgresql
* @subpackage tests
*/
class PostgreSQLDatabaseTest extends SapphireTest
{
protected $usesDatabase = true;
public function testReadOnlyTransaction()
{
if (DB::get_conn()->supportsTransactions() == true
&& DB::get_conn() instanceof PostgreSQLDatabase
) {
$page = new Page();
$page->Title = 'Read only success';
$page->write();
DB::get_conn()->transactionStart('READ ONLY');
try {
$page = new Page();
$page->Title = 'Read only page failed';
$page->write();
} catch (Exception $e) {
//could not write this record
//We need to do a rollback or a commit otherwise we'll get error messages
DB::get_conn()->transactionRollback();
}
DB::get_conn()->transactionEnd();
DataObject::flush_and_destroy_cache();
$success = DataObject::get('Page', "\"Title\"='Read only success'");
$fail = DataObject::get('Page', "\"Title\"='Read only page failed'");
//This page should be in the system
$this->assertTrue(is_object($success) && $success->exists());
//This page should NOT exist, we had 'read only' permissions
$this->assertFalse(is_object($fail) && $fail->exists());
} else {
$this->markTestSkipped('Current database is not PostgreSQL');
}
}
}

View File

@ -0,0 +1,138 @@
<?php
namespace SilverStripe\PostgreSQL\Tests;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\Connect\Database;
use SilverStripe\ORM\Connect\DatabaseException;
use SilverStripe\ORM\DB;
use SilverStripe\PostgreSQL\PostgreSQLConnector;
use SilverStripe\PostgreSQL\PostgreSQLSchemaManager;
class PostgreSQLSchemaManagerTest extends SapphireTest
{
protected $usesTransactions = false;
public function testAlterTable()
{
try {
/** @var PostgreSQLSchemaManager $dbSchema */
$dbSchema = DB::get_schema();
$dbSchema->quiet();
$this->createSS3Table();
try {
DB::query('INSERT INTO "ClassNamesUpgrade" ("ClassName") VALUES (\'App\MySite\FooBar\')');
$this->assertFalse(true, 'SS3 Constaint should have blocked the previous insert.');
} catch (DatabaseException $ex) {
}
$dbSchema->schemaUpdate(function () use ($dbSchema) {
$dbSchema->requireTable(
'ClassNamesUpgrade',
[
'ID' => 'PrimaryKey',
'ClassName' => 'Enum(array("App\\\\MySite\\\\FooBar"))',
]
);
});
DB::query('INSERT INTO "ClassNamesUpgrade" ("ClassName") VALUES (\'App\MySite\FooBar\')');
$count = DB::query('SELECT count(*) FROM "ClassNamesUpgrade" WHERE "ClassName" = \'App\MySite\FooBar\'')
->value();
$this->assertEquals(1, $count);
} finally {
DB::query('DROP TABLE IF EXISTS "ClassNamesUpgrade"');
DB::query('DROP SEQUENCE IF EXISTS "ClassNamesUpgrade_ID_seq"');
}
}
private function createSS3Table()
{
DB::query(<<<SQL
CREATE SEQUENCE "ClassNamesUpgrade_ID_seq" start 1 increment 1;
CREATE TABLE "ClassNamesUpgrade"
(
"ID" bigint NOT NULL DEFAULT nextval('"ClassNamesUpgrade_ID_seq"'::regclass),
"ClassName" character varying(255) DEFAULT 'ClassNamesUpgrade'::character varying,
CONSTRAINT "ClassNamesUpgrade_pkey" PRIMARY KEY ("ID"),
CONSTRAINT "ClassNamesUpgrade_ClassName_check" CHECK ("ClassName"::text = ANY (ARRAY['FooBar'::character varying::text]))
)
WITH (
OIDS=FALSE
);
SQL
);
}
public function testRenameTable()
{
try {
/** @var PostgreSQLSchemaManager $dbSchema */
$dbSchema = DB::get_schema();
$dbSchema->quiet();
$this->createSS3VersionedTable();
$this->assertConstraintCount(1, 'ClassNamesUpgrade_versioned_ClassName_check');
$dbSchema->schemaUpdate(function () use ($dbSchema) {
$dbSchema->renameTable(
'ClassNamesUpgrade_versioned',
'ClassNamesUpgrade_Versioned'
);
});
$this->assertTableCount(0, 'ClassNamesUpgrade_versioned');
$this->assertTableCount(1, 'ClassNamesUpgrade_Versioned');
$this->assertConstraintCount(0, 'ClassNamesUpgrade_versioned_ClassName_check');
$this->assertConstraintCount(1, 'ClassNamesUpgrade_Versioned_ClassName_check');
} finally {
DB::query('DROP TABLE IF EXISTS "ClassNamesUpgrade_Versioned"');
DB::query('DROP TABLE IF EXISTS "ClassNamesUpgrade_versioned"');
DB::query('DROP SEQUENCE IF EXISTS "ClassNamesUpgrade_versioned_ID_seq"');
}
}
private function assertConstraintCount($expected, $constraintName)
{
$count = DB::prepared_query(
'SELECT count(*) FROM pg_catalog.pg_constraint WHERE conname like ?',
[$constraintName]
)->value();
$this->assertEquals($expected, $count);
}
private function assertTableCount($expected, $tableName)
{
$count = DB::prepared_query(
'SELECT count(*) FROM pg_catalog.pg_tables WHERE "tablename" like ?',
[$tableName]
)->value();
$this->assertEquals($expected, $count);
}
private function createSS3VersionedTable()
{
DB::query(<<<SQL
CREATE SEQUENCE "ClassNamesUpgrade_versioned_ID_seq" start 1 increment 1;
CREATE TABLE "ClassNamesUpgrade_versioned"
(
"ID" bigint NOT NULL DEFAULT nextval('"ClassNamesUpgrade_versioned_ID_seq"'::regclass),
"ClassName" character varying(255) DEFAULT 'ClassNamesUpgrade'::character varying,
CONSTRAINT "ClassNamesUpgrade_pkey" PRIMARY KEY ("ID"),
CONSTRAINT "ClassNamesUpgrade_versioned_ClassName_check" CHECK ("ClassName"::text = ANY (ARRAY['FooBar'::character varying::text]))
)
WITH (
OIDS=FALSE
);
SQL
);
}
}