From b55ce1d20368e0136ab522f3b7b1639eb9a5cbc2 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 20 Jan 2023 15:10:29 +0100 Subject: [PATCH] Support Postgres database for Kolab cache --- plugins/libkolab/SQL/postgres.initial.sql | 207 ++++++++++++++++++ .../libkolab/lib/kolab_storage_dav_cache.php | 24 +- 2 files changed, 220 insertions(+), 11 deletions(-) create mode 100644 plugins/libkolab/SQL/postgres.initial.sql diff --git a/plugins/libkolab/SQL/postgres.initial.sql b/plugins/libkolab/SQL/postgres.initial.sql new file mode 100644 index 00000000..df396883 --- /dev/null +++ b/plugins/libkolab/SQL/postgres.initial.sql @@ -0,0 +1,207 @@ +CREATE SEQUENCE kolab_folders_seq + INCREMENT BY 1 + NO MAXVALUE + NO MINVALUE + CACHE 1; + +CREATE TABLE kolab_folders ( + folder_id integer DEFAULT nextval('kolab_folders_seq'::text) PRIMARY KEY, + resource varchar(255) NOT NULL, + "type" varchar(32) NOT NULL, + synclock integer NOT NULL DEFAULT 0, + ctag varchar(40) DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + objectcount integer DEFAULT NULL +); + +CREATE INDEX kolab_folders_resource_type_idx ON kolab_folders(resource, "type"); + +CREATE TABLE kolab_cache_contact ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + "type" varchar(32) NOT NULL, + name varchar(255) NOT NULL, + firstname varchar(255) NOT NULL, + surname varchar(255) NOT NULL, + email varchar(255) NOT NULL, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_contact_type_idx ON kolab_cache_contact(folder_id, "type"); +CREATE INDEX kolab_cache_contact_uid2msguid_idx ON kolab_cache_contact(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_event ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_event_uid2msguid_idx ON kolab_cache_event(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_task ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_task_uid2msguid_idx ON kolab_cache_task(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_journal ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_journal_uid2msguid_idx ON kolab_cache_journal(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_note ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_note_uid2msguid_idx ON kolab_cache_note(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_file ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + filename varchar(255) DEFAULT NULL, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_file_filename_idx ON kolab_cache_file(folder_id, filename); +CREATE INDEX kolab_cache_file_uid2msguid_idx ON kolab_cache_file(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_configuration ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + "type" varchar(32) NOT NULL, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_configuration_type_idx ON kolab_cache_configuration(folder_id, "type"); +CREATE INDEX kolab_cache_configuration_uid2msguid_idx ON kolab_cache_configuration(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_freebusy ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + msguid integer NOT NULL, + uid varchar(512) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, msguid) +); + +CREATE INDEX kolab_cache_freebusy_uid2msguid_idx ON kolab_cache_freebusy(folder_id, uid, msguid); + +CREATE TABLE kolab_cache_dav_contact ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + uid varchar(512) NOT NULL, + etag varchar(128) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + "type" varchar(32) NOT NULL, + name varchar(255) NOT NULL, + firstname varchar(255) NOT NULL, + surname varchar(255) NOT NULL, + email varchar(255) NOT NULL, + PRIMARY KEY(folder_id, uid) +); + +CREATE INDEX kolab_cache_dav_contact_type_idx ON kolab_cache_dav_contact(folder_id, "type"); + +CREATE TABLE kolab_cache_dav_event ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + uid varchar(512) NOT NULL, + etag varchar(128) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, uid) +); + +CREATE TABLE kolab_cache_dav_task ( + folder_id integer NOT NULL + REFERENCES kolab_folders (folder_id) ON DELETE CASCADE ON UPDATE CASCADE, + uid varchar(512) NOT NULL, + etag varchar(128) NOT NULL, + created timestamp with time zone DEFAULT NULL, + changed timestamp with time zone DEFAULT NULL, + data text NOT NULL, + tags text NOT NULL, + words text NOT NULL, + dtstart timestamp with time zone, + dtend timestamp with time zone, + PRIMARY KEY(folder_id, uid) +); + +INSERT INTO "system" (name, "value") VALUES ('libkolab-version', '2022122800'); diff --git a/plugins/libkolab/lib/kolab_storage_dav_cache.php b/plugins/libkolab/lib/kolab_storage_dav_cache.php index 68a97708..2bc4b317 100644 --- a/plugins/libkolab/lib/kolab_storage_dav_cache.php +++ b/plugins/libkolab/lib/kolab_storage_dav_cache.php @@ -533,9 +533,8 @@ class kolab_storage_dav_cache extends kolab_storage_cache if ($object) { $sql_data = $this->_serialize($object); - // Skip multi-folder insert for all databases but MySQL - // In Oracle we can't put long data inline, others we don't support yet - if (strpos($this->db->db_provider, 'mysql') !== 0) { + // Skip multi-folder insert for all databases but MySQL and Postgres + if (!preg_match('/^(mysql|postgres)/', $this->db->db_provider)) { $extra_args = []; $params = [ $this->folder_id, @@ -588,17 +587,20 @@ class kolab_storage_dav_cache extends kolab_storage_cache if ($buffer && ($force || (strlen($buffer) + strlen($line) > $this->max_sql_packet()))) { $columns = implode(', ', array_map(function($n) { return "`{$n}`"; }, $cols)); - $update = implode(', ', array_map(function($i) { return "`{$i}` = VALUES(`{$i}`)"; }, array_slice($cols, 2))); - $result = $this->db->query( - "INSERT INTO `{$this->cache_table}` ($columns) VALUES $buffer" - . " ON DUPLICATE KEY UPDATE $update" - ); + if ($this->db->db_provider == 'postgres') { + $update = "ON CONFLICT (folder_id, uid) DO UPDATE SET " + . implode(', ', array_map(function($i) { return "`{$i}` = EXCLUDED.`{$i}`"; }, array_slice($cols, 2))); + } + else { + $update = "ON DUPLICATE KEY UPDATE " + . implode(', ', array_map(function($i) { return "`{$i}` = VALUES(`{$i}`)"; }, array_slice($cols, 2))); + } + + $result = $this->db->query("INSERT INTO `{$this->cache_table}` ($columns) VALUES $buffer $update"); if (!$this->db->affected_rows($result)) { - rcube::raise_error(array( - 'code' => 900, 'message' => "Failed to write to kolab cache" - ), true); + rcube::raise_error(['code' => 900, 'message' => "Failed to write to kolab cache"], true); } $buffer = '';