Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proper PostgreSQL support #825

Merged
merged 22 commits into from
Apr 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ env:

services:
- mysql
- postgresql

sudo: false

before_script:
- mysql -e 'create database sabredav_test'
- psql -c "create database sabredav_test" -U postgres
- psql -c "create user sabredav with PASSWORD 'sabredav';GRANT ALL PRIVILEGES ON DATABASE sabredav_test TO sabredav" -U postgres
- phpenv config-rm xdebug.ini; true
# - composer self-update
- composer update --prefer-dist $LOWEST_DEPS

add-ons:
apt:
packages:
- postgresql-9.5
- postgresql-contrib-9.5

script:
- ./bin/phpunit --configuration tests/phpunit.xml.dist $TEST_DEPS
- ./bin/sabre-cs-fixer fix lib/ --dry-run --diff
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ChangeLog
others to their calendar and give them read/read-write access!
* #397: Support for PSR-3. You can now log exceptions with your favourite
psr3-compatible logging tool.
* #825: Actual proper, tested support for PostgreSQL. We require version 9.5.
* Removed database migration script for sabre/dav 1.7. To update from that
version you now first need to update to sabre/dav 3.1.
* Removed deprecated function: `Sabre\DAV\Auth\Plugin::getCurrentUser()`.
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ CREATE UNIQUE INDEX addressbooks_ukey
CREATE TABLE cards (
id SERIAL NOT NULL,
addressbookid INTEGER NOT NULL,
carddata TEXT,
carddata BYTEA,
uri VARCHAR(200),
lastmodified INTEGER,
etag VARCHAR(32),
Expand All @@ -29,10 +29,6 @@ ALTER TABLE ONLY cards
CREATE UNIQUE INDEX cards_ukey
ON cards USING btree (addressbookid, uri);

ALTER TABLE ONLY cards
ADD CONSTRAINT cards_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id)
ON DELETE CASCADE;

CREATE TABLE addressbookchanges (
id SERIAL NOT NULL,
uri VARCHAR(200) NOT NULL,
Expand All @@ -46,7 +42,3 @@ ALTER TABLE ONLY addressbookchanges

CREATE INDEX addressbookchanges_addressbookid_synctoken_ix
ON addressbookchanges USING btree (addressbookid, synctoken);

ALTER TABLE ONLY addressbookchanges
ADD CONSTRAINT addressbookchanges_addressbookid_fkey FOREIGN KEY (addressbookid) REFERENCES addressbooks(id)
ON DELETE CASCADE;
70 changes: 41 additions & 29 deletions examples/sql/pgsql.calendars.sql
Original file line number Diff line number Diff line change
@@ -1,27 +1,6 @@
CREATE TABLE calendars (
id SERIAL NOT NULL,
principaluri VARCHAR(100),
displayname VARCHAR(100),
uri VARCHAR(200),
synctoken INTEGER NOT NULL DEFAULT 1,
description TEXT,
calendarorder INTEGER NOT NULL DEFAULT 0,
calendarcolor VARCHAR(10),
timezone TEXT,
components VARCHAR(21),
uid VARCHAR(200),
transparent SMALLINT NOT NULL DEFAULT '0'
);

ALTER TABLE ONLY calendars
ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);

CREATE UNIQUE INDEX calendars_ukey
ON calendars USING btree (principaluri, uri);

CREATE TABLE calendarobjects (
id SERIAL NOT NULL,
calendardata TEXT,
calendardata BYTEA,
uri VARCHAR(200),
calendarid INTEGER NOT NULL,
lastmodified INTEGER,
Expand All @@ -39,9 +18,46 @@ ALTER TABLE ONLY calendarobjects
CREATE UNIQUE INDEX calendarobjects_ukey
ON calendarobjects USING btree (calendarid, uri);

ALTER TABLE ONLY calendarobjects
ADD CONSTRAINT calendarobjects_calendarid_fkey FOREIGN KEY (calendarid) REFERENCES calendars(id)
ON DELETE CASCADE;

CREATE TABLE calendars (
id SERIAL NOT NULL,
synctoken INTEGER NOT NULL DEFAULT 1,
components VARCHAR(21)
);

ALTER TABLE ONLY calendars
ADD CONSTRAINT calendars_pkey PRIMARY KEY (id);


CREATE TABLE calendarinstances (
id SERIAL NOT NULL,
calendarid INTEGER NOT NULL,
principaluri VARCHAR(100),
access SMALLINT NOT NULL DEFAULT '1', -- '1 = owner, 2 = read, 3 = readwrite'
displayname VARCHAR(100),
uri VARCHAR(200),
description TEXT,
calendarorder INTEGER NOT NULL DEFAULT 0,
calendarcolor VARCHAR(10),
timezone TEXT,
transparent SMALLINT NOT NULL DEFAULT '0',
share_href VARCHAR(100),
share_displayname VARCHAR(100),
share_invitestatus SMALLINT NOT NULL DEFAULT '2' -- '1 = noresponse, 2 = accepted, 3 = declined, 4 = invalid'
);

ALTER TABLE ONLY calendarinstances
ADD CONSTRAINT calendarinstances_pkey PRIMARY KEY (id);

CREATE UNIQUE INDEX calendarinstances_principaluri_uri
ON calendarinstances USING btree (principaluri, uri);


CREATE UNIQUE INDEX calendarinstances_principaluri_calendarid
ON calendarinstances USING btree (principaluri, calendarid);

CREATE UNIQUE INDEX calendarinstances_principaluri_share_href
ON calendarinstances USING btree (principaluri, share_href);

CREATE TABLE calendarsubscriptions (
id SERIAL NOT NULL,
Expand Down Expand Up @@ -78,10 +94,6 @@ ALTER TABLE ONLY calendarchanges
CREATE INDEX calendarchanges_calendarid_synctoken_ix
ON calendarchanges USING btree (calendarid, synctoken);

ALTER TABLE ONLY calendarchanges
ADD CONSTRAINT calendarchanges_calendar_fk FOREIGN KEY (calendarid) REFERENCES calendars(id)
ON DELETE CASCADE;

CREATE TABLE schedulingobjects (
id SERIAL NOT NULL,
principaluri VARCHAR(255),
Expand Down
8 changes: 0 additions & 8 deletions examples/sql/pgsql.principals.sql
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,6 @@ ALTER TABLE ONLY groupmembers
CREATE UNIQUE INDEX groupmembers_ukey
ON groupmembers USING btree (principal_id, member_id);

ALTER TABLE ONLY groupmembers
ADD CONSTRAINT groupmembers_principal_id_fkey FOREIGN KEY (principal_id) REFERENCES principals(id)
ON DELETE CASCADE;

ALTER TABLE ONLY groupmembers
ADD CONSTRAINT groupmembers_member_id_id_fkey FOREIGN KEY (member_id) REFERENCES principals(id)
ON DELETE CASCADE;

INSERT INTO principals (uri,email,displayname) VALUES
('principals/admin', '[email protected]','Administrator'),
('principals/admin/calendar-proxy-read', null, null),
Expand Down
2 changes: 1 addition & 1 deletion examples/sql/pgsql.propertystorage.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CREATE TABLE propertystorage (
path VARCHAR(1024) NOT NULL,
name VARCHAR(100) NOT NULL,
valuetype INT,
value TEXT
value BYTEA
);

ALTER TABLE ONLY propertystorage
Expand Down
21 changes: 15 additions & 6 deletions lib/CalDAV/Backend/PDO.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,10 @@ function createCalendar($principalUri, $calendarUri, array $properties) {
$stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (synctoken, components) VALUES (1, ?)");
$stmt->execute([$components]);

$calendarId = $this->pdo->lastInsertId();
$calendarId = $this->pdo->lastInsertId(
$this->calendarTableName . '_id_seq'
);

$values[':calendarid'] = $calendarId;

foreach ($this->propertyMap as $xmlName => $dbName) {
Expand All @@ -273,9 +276,13 @@ function createCalendar($principalUri, $calendarUri, array $properties) {
}

$stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarInstancesTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");

$stmt->execute($values);

return [$calendarId, $this->pdo->lastInsertId()];
return [
$calendarId,
$this->pdo->lastInsertId($this->calendarInstancesTableName . '_id_seq')
];

}

Expand Down Expand Up @@ -433,7 +440,7 @@ function getCalendarObjects($calendarId) {
$result[] = [
'id' => $row['id'],
'uri' => $row['uri'],
'lastmodified' => $row['lastmodified'],
'lastmodified' => (int)$row['lastmodified'],
'etag' => '"' . $row['etag'] . '"',
'size' => (int)$row['size'],
'component' => strtolower($row['componenttype']),
Expand Down Expand Up @@ -476,7 +483,7 @@ function getCalendarObject($calendarId, $objectUri) {
return [
'id' => $row['id'],
'uri' => $row['uri'],
'lastmodified' => $row['lastmodified'],
'lastmodified' => (int)$row['lastmodified'],
'etag' => '"' . $row['etag'] . '"',
'size' => (int)$row['size'],
'calendardata' => $row['calendardata'],
Expand Down Expand Up @@ -518,7 +525,7 @@ function getMultipleCalendarObjects($calendarId, array $uris) {
$result[] = [
'id' => $row['id'],
'uri' => $row['uri'],
'lastmodified' => $row['lastmodified'],
'lastmodified' => (int)$row['lastmodified'],
'etag' => '"' . $row['etag'] . '"',
'size' => (int)$row['size'],
'calendardata' => $row['calendardata'],
Expand Down Expand Up @@ -1154,7 +1161,9 @@ function createSubscription($principalUri, $uri, array $properties) {
$stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")");
$stmt->execute($values);

return $this->pdo->lastInsertId();
return $this->pdo->lastInsertId(
$this->calendarSubscriptionsTableName . '_id_seq'
);

}

Expand Down
9 changes: 7 additions & 2 deletions lib/CardDAV/Backend/PDO.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) {
} else {
$query .= ', ';
}
$query .= ' `' . $key . '` = :' . $key . ' ';
$query .= ' ' . $key . ' = :' . $key . ' ';
}
$query .= ' WHERE id = :addressbookid';

Expand Down Expand Up @@ -180,7 +180,9 @@ function createAddressBook($principalUri, $url, array $properties) {
$query = 'INSERT INTO ' . $this->addressBooksTableName . ' (uri, displayname, description, principaluri, synctoken) VALUES (:uri, :displayname, :description, :principaluri, 1)';
$stmt = $this->pdo->prepare($query);
$stmt->execute($values);
return $this->pdo->lastInsertId();
return $this->pdo->lastInsertId(
$this->addressBooksTableName . '_id_seq'
);

}

Expand Down Expand Up @@ -230,6 +232,7 @@ function getCards($addressbookId) {
$result = [];
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$row['etag'] = '"' . $row['etag'] . '"';
$row['lastmodified'] = (int)$row['lastmodified'];
$result[] = $row;
}
return $result;
Expand Down Expand Up @@ -258,6 +261,7 @@ function getCard($addressBookId, $cardUri) {
if (!$result) return false;

$result['etag'] = '"' . $result['etag'] . '"';
$result['lastmodified'] = (int)$result['lastmodified'];
return $result;

}
Expand Down Expand Up @@ -286,6 +290,7 @@ function getMultipleCards($addressBookId, array $uris) {
$result = [];
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
$row['etag'] = '"' . $row['etag'] . '"';
$row['lastmodified'] = (int)$row['lastmodified'];
$result[] = $row;
}
return $result;
Expand Down
33 changes: 31 additions & 2 deletions lib/DAV/PropertyStorage/Backend/PDO.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ function propFind($path, PropFind $propFind) {
$stmt->execute([$path]);

while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
if (gettype($row['value']) === 'resource') {
$row['value'] = stream_get_contents($row['value']);
}
switch ($row['valuetype']) {
case null :
case self::VT_STRING :
Expand Down Expand Up @@ -121,7 +124,26 @@ function propPatch($path, PropPatch $propPatch) {

$propPatch->handleRemaining(function($properties) use ($path) {

$updateStmt = $this->pdo->prepare("REPLACE INTO " . $this->tableName . " (path, name, valuetype, value) VALUES (?, ?, ?, ?)");

if ($this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME) === 'pgsql') {

$updateSql = <<<SQL
INSERT INTO {$this->tableName} (path, name, valuetype, value)
VALUES (:path, :name, :valuetype, :value)
ON CONFLICT (path, name)
DO UPDATE SET valuetype = :valuetype, value = :value
SQL;


} else {
$updateSql = <<<SQL
REPLACE INTO {$this->tableName} (path, name, valuetype, value)
VALUES (:path, :name, :valuetype, :value)
SQL;

}

$updateStmt = $this->pdo->prepare($updateSql);
$deleteStmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? AND name = ?");

foreach ($properties as $name => $value) {
Expand All @@ -136,7 +158,14 @@ function propPatch($path, PropPatch $propPatch) {
$valueType = self::VT_OBJECT;
$value = serialize($value);
}
$updateStmt->execute([$path, $name, $valueType, $value]);

$updateStmt->bindParam('path', $path, \PDO::PARAM_STR);
$updateStmt->bindParam('name', $name, \PDO::PARAM_STR);
$updateStmt->bindParam('valuetype', $valueType, \PDO::PARAM_INT);
$updateStmt->bindParam('value', $value, \PDO::PARAM_LOB);

$updateStmt->execute();

} else {
$deleteStmt->execute([$path, $name]);
}
Expand Down
Loading