Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
Pgsql
100.00% covered (success)
100.00%
21 / 21
100.00% covered (success)
100.00%
2 / 2
13
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createTables
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
12
1<?php
2
3/**
4 * The phpMyFAQ instances a database class with CREATE TABLE statements for PostgreSQL.
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2015-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2015-04-16
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Instance\Database;
21
22use phpMyFAQ\Configuration;
23use phpMyFAQ\Instance\Database;
24
25/**
26 * Class Pgsql
27 *
28 * @package phpMyFAQ\Instance\Database
29 * @deprecated Use PDO instead. Will be removed in the v5.0 release.
30 */
31class Pgsql extends Database implements DriverInterface
32{
33    /** @var array<string, string> */
34    private array $createTableStatements = [
35        'faquser_user_id_seq' => 'CREATE SEQUENCE %sfaquser_user_id_seq START WITH 2',
36        'faqadminlog' => 'CREATE TABLE %sfaqadminlog (
37            id SERIAL NOT NULL,
38            time INTEGER NOT NULL,
39            usr INTEGER NOT NULL,
40            text TEXT NOT NULL,
41            hash VARCHAR(64) NULL,
42            previous_hash VARCHAR(64) NULL,
43            ip VARCHAR(64) NOT NULL,
44            PRIMARY KEY (id))',
45        'faqattachment' => 'CREATE TABLE %sfaqattachment (
46            id SERIAL NOT NULL,
47            record_id INTEGER NOT NULL,
48            record_lang VARCHAR(5) NOT NULL,
49            real_hash CHAR(32) NOT NULL,
50            virtual_hash CHAR(32) NOT NULL,
51            password_hash CHAR(40) NULL,
52            filename VARCHAR(255) NOT NULL,
53            filesize INTEGER NOT NULL,
54            encrypted INTEGER NOT NULL DEFAULT 0,
55            mime_type VARCHAR(255) NULL,
56            PRIMARY KEY (id))',
57        'faqattachment_file' => 'CREATE TABLE %sfaqattachment_file (
58            virtual_hash CHAR(32) NOT NULL,
59            contents BYTEA,
60            PRIMARY KEY (virtual_hash))',
61        'faqbackup' => 'CREATE TABLE %sfaqbackup (
62            id INTEGER NOT NULL,
63            filename VARCHAR(255) NOT NULL,
64            authkey VARCHAR(255) NOT NULL,
65            authcode VARCHAR(255) NOT NULL,
66            created timestamp NOT NULL,
67            PRIMARY KEY (id))',
68        'faqbookmarks' => 'CREATE TABLE %sfaqbookmarks (
69            userid INTEGER DEFAULT NULL,
70            faqid INTEGER DEFAULT NULL)',
71        'faqcaptcha' => 'CREATE TABLE %sfaqcaptcha (
72            id VARCHAR(6) NOT NULL,
73            useragent VARCHAR(255) NOT NULL,
74            language VARCHAR(5) NOT NULL,
75            ip VARCHAR(64) NOT NULL,
76            captcha_time INTEGER NOT NULL,
77            PRIMARY KEY (id))',
78        'faqcategories' => 'CREATE TABLE %sfaqcategories (
79            id SERIAL NOT NULL,
80            lang VARCHAR(5) NOT NULL,
81            parent_id INTEGER NOT NULL,
82            name VARCHAR(255) NOT NULL,
83            description VARCHAR(255) DEFAULT NULL,
84            user_id INTEGER NOT NULL,
85            group_id INTEGER NOT NULL DEFAULT -1,
86            active INTEGER NULL DEFAULT 1,
87            image VARCHAR(255) DEFAULT NULL,
88            show_home INTEGER DEFAULT NULL,
89            PRIMARY KEY (id, lang))',
90        'faqcategoryrelations' => 'CREATE TABLE %sfaqcategoryrelations (
91            category_id INTEGER NOT NULL,
92            category_lang VARCHAR(5) NOT NULL,
93            record_id INTEGER NOT NULL,
94            record_lang VARCHAR(5) NOT NULL,
95            PRIMARY KEY (category_id, category_lang, record_id, record_lang))',
96        'idx_records' => 'CREATE INDEX idx_records_%s ON %sfaqcategoryrelations
97            (record_id, record_lang)',
98        'faqcategory_group' => 'CREATE TABLE %sfaqcategory_group (
99            category_id INTEGER NOT NULL,
100            group_id INTEGER NOT NULL,
101            PRIMARY KEY (category_id, group_id))',
102        'faqcategory_news' => 'CREATE TABLE %sfaqcategory_news (
103            category_id INTEGER NOT NULL,
104            news_id INTEGER NOT NULL,
105            PRIMARY KEY (category_id, news_id))',
106        'faqcategory_order' => 'CREATE TABLE %sfaqcategory_order (
107            category_id INTEGER NOT NULL,
108            parent_id INTEGER DEFAULT NULL,
109            position INTEGER NOT NULL,
110            PRIMARY KEY (category_id))',
111        'faqcategory_order_position_seq' => 'CREATE SEQUENCE %sfaqcategory_order_position_seq START WITH 1',
112        'faqcategory_user' => 'CREATE TABLE %sfaqcategory_user (
113            category_id INTEGER NOT NULL,
114            user_id INTEGER NOT NULL,
115            PRIMARY KEY (category_id, user_id))',
116        'faqchanges' => 'CREATE TABLE %sfaqchanges (
117            id SERIAL NOT NULL,
118            beitrag SMALLINT NOT NULL,
119            lang VARCHAR(5) NOT NULL,
120            revision_id INTEGER NOT NULL DEFAULT 0,
121            usr INTEGER NOT NULL ,
122            datum INTEGER NOT NULL,
123            what text DEFAULT NULL,
124            PRIMARY KEY (id, lang))',
125        'faqcomments' => 'CREATE TABLE %sfaqcomments (
126            id_comment SERIAL NOT NULL,
127            id INTEGER NOT NULL,
128            type VARCHAR(10) NOT NULL,
129            usr VARCHAR(255) NOT NULL,
130            email VARCHAR(255) NOT NULL,
131            comment text NOT NULL,
132            datum VARCHAR(64) NOT NULL,
133            helped text DEFAULT NULL,
134            PRIMARY KEY (id_comment))',
135        'faqconfig' => 'CREATE TABLE %sfaqconfig (
136            config_name VARCHAR(255) NOT NULL default \'\',
137            config_value TEXT DEFAULT NULL,
138            PRIMARY KEY (config_name))',
139        'faqdata' => 'CREATE TABLE %sfaqdata (
140            id SERIAL NOT NULL,
141            lang VARCHAR(5) NOT NULL,
142            solution_id INTEGER NOT NULL,
143            revision_id INTEGER NOT NULL DEFAULT 0,
144            active VARCHAR(3) NOT NULL,
145            sticky INTEGER NOT NULL,
146            keywords TEXT DEFAULT NULL,
147            thema TEXT NOT NULL,
148            content TEXT DEFAULT NULL,
149            author VARCHAR(255) NOT NULL,
150            email VARCHAR(255) NOT NULL,
151            comment char(1) default \'y\',
152            updated VARCHAR(15) NOT NULL,
153            date_start VARCHAR(14) NOT NULL DEFAULT \'00000000000000\',
154            date_end VARCHAR(14) NOT NULL DEFAULT \'99991231235959\',
155            created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
156            notes text DEFAULT NULL,
157            sticky_order INTEGER DEFAULT NULL,
158            PRIMARY KEY (id, lang))',
159        'faqdata_revisions' => 'CREATE TABLE %sfaqdata_revisions (
160            id SERIAL NOT NULL,
161            lang VARCHAR(5) NOT NULL,
162            solution_id INTEGER NOT NULL,
163            revision_id INTEGER NOT NULL DEFAULT 0,
164            active VARCHAR(3) NOT NULL,
165            sticky INTEGER NOT NULL,
166            keywords TEXT DEFAULT NULL,
167            thema TEXT NOT NULL,
168            content TEXT DEFAULT NULL,
169            author VARCHAR(255) NOT NULL,
170            email VARCHAR(255) NOT NULL,
171            comment char(1) default \'y\',
172            updated VARCHAR(15) NOT NULL,
173            date_start VARCHAR(14) NOT NULL DEFAULT \'00000000000000\',
174            date_end VARCHAR(14) NOT NULL DEFAULT \'99991231235959\',
175            created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
176            notes text DEFAULT NULL,
177            sticky_order INTEGER DEFAULT NULL,
178            PRIMARY KEY (id, lang, solution_id, revision_id))',
179        'faqdata_group' => 'CREATE TABLE %sfaqdata_group (
180            record_id INTEGER NOT NULL,
181            group_id INTEGER NOT NULL,
182            PRIMARY KEY (record_id, group_id))',
183        'faqdata_tags' => 'CREATE TABLE %sfaqdata_tags (
184            record_id INTEGER NOT NULL,
185            tagging_id INTEGER NOT NULL,
186            PRIMARY KEY (record_id, tagging_id))',
187        'faqdata_user' => 'CREATE TABLE %sfaqdata_user (
188            record_id INTEGER NOT NULL,
189            user_id INTEGER NOT NULL,
190            PRIMARY KEY (record_id, user_id))',
191        'faqforms' => 'CREATE TABLE %sfaqforms (
192            form_id INTEGER NOT NULL,
193            input_id INTEGER NOT NULL,
194            input_type VARCHAR(1000) NOT NULL,
195            input_label VARCHAR(500) NOT NULL,
196            input_active INTEGER NOT NULL,
197            input_required INTEGER NOT NULL,
198            input_lang VARCHAR(11) NOT NULL)',
199        'faqglossary' => 'CREATE TABLE %sfaqglossary (
200            id SERIAL NOT NULL ,
201            lang VARCHAR(5) NOT NULL ,
202            item VARCHAR(255) NOT NULL ,
203            definition text NOT NULL,
204            PRIMARY KEY (id, lang))',
205        'faqgroup' => 'CREATE TABLE %sfaqgroup (
206            group_id SERIAL NOT NULL,
207            name VARCHAR(25) NULL,
208            description text NULL,
209            auto_join INTEGER NULL,
210            PRIMARY KEY(group_id))',
211        'faqgroup_right' => 'CREATE TABLE %sfaqgroup_right (
212            group_id INTEGER NOT NULL,
213            right_id INTEGER NOT NULL,
214            PRIMARY KEY(group_id, right_id))',
215        'faqinstances' => 'CREATE TABLE %sfaqinstances (
216            id SERIAL NOT NULL,
217            url VARCHAR(255) NOT NULL,
218            instance VARCHAR(255) NOT NULL,
219            comment TEXT NULL,
220            created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
221            modified TIMESTAMP NOT NULL,
222            PRIMARY KEY (id))',
223        'faqinstances_config' => 'CREATE TABLE %sfaqinstances_config (
224            instance_id INTEGER NOT NULL,
225            config_name VARCHAR(255) NOT NULL default \'\',
226            config_value VARCHAR(255) DEFAULT NULL,
227            PRIMARY KEY (instance_id, config_name))',
228        'faqnews' => 'CREATE TABLE %sfaqnews (
229            id SERIAL NOT NULL,
230            lang VARCHAR(5) NOT NULL,
231            header VARCHAR(255) NOT NULL,
232            artikel text NOT NULL,
233            datum VARCHAR(14) NOT NULL,
234            author_name  VARCHAR(255) NULL,
235            author_email VARCHAR(255) NULL,
236            active char(1) default \'y\',
237            comment char(1) default \'n\',
238            link VARCHAR(255) DEFAULT NULL,
239            linktitel VARCHAR(255) DEFAULT NULL,
240            target VARCHAR(255) NOT NULL,
241            PRIMARY KEY (id))',
242        'faqcustompages' => 'CREATE TABLE %sfaqcustompages (
243            id INTEGER NOT NULL,
244            lang VARCHAR(5) NOT NULL,
245            page_title VARCHAR(255) NOT NULL,
246            slug VARCHAR(255) NOT NULL,
247            content TEXT NOT NULL,
248            author_name VARCHAR(255) NOT NULL,
249            author_email VARCHAR(255) NOT NULL,
250            active CHAR(1) NOT NULL DEFAULT \'n\',
251            created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
252            updated TIMESTAMP NULL,
253            seo_title VARCHAR(60) NULL,
254            seo_description VARCHAR(160) NULL,
255            seo_robots VARCHAR(50) NOT NULL DEFAULT \'index,follow\',
256            PRIMARY KEY (id, lang))',
257        'faqquestions' => 'CREATE TABLE %sfaqquestions (
258            id SERIAL NOT NULL,
259            lang VARCHAR(5) NOT NULL,
260            username VARCHAR(100) NOT NULL,
261            email VARCHAR(100) NOT NULL,
262            category_id INTEGER NOT NULL,
263            question text NOT NULL,
264            created VARCHAR(20) NOT NULL,
265            is_visible char(1) default \'Y\',
266            answer_id INTEGER NOT NULL DEFAULT 0,
267            PRIMARY KEY (id))',
268        'faqright' => 'CREATE TABLE %sfaqright (
269            right_id SERIAL NOT NULL,
270            name VARCHAR(50) NULL,
271            description text NULL,
272            for_users INTEGER NULL DEFAULT 1,
273            for_groups INTEGER NULL DEFAULT 1,
274            for_sections INTEGER NULL DEFAULT 1,
275            PRIMARY KEY (right_id))',
276        'faqsearches' => 'CREATE TABLE %sfaqsearches (
277            id SERIAL NOT NULL ,
278            lang VARCHAR(5) NOT NULL ,
279            searchterm VARCHAR(255) NOT NULL ,
280            searchdate TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
281            PRIMARY KEY (id, lang))',
282        'faqseo' => 'CREATE TABLE %sfaqseo (
283            id SERIAL NOT NULL,
284            type VARCHAR(32) NOT NULL,
285            reference_id INTEGER NOT NULL,
286            reference_language VARCHAR(5) NOT NULL,
287            title TEXT,
288            description TEXT,
289            slug TEXT,
290            created DATE NOT NULL DEFAULT CURRENT_DATE,
291            PRIMARY KEY (id))',
292        'faqsessions' => 'CREATE TABLE %sfaqsessions (
293            sid SERIAL NOT NULL,
294            user_id INTEGER NOT NULL,
295            ip VARCHAR(64) NOT NULL,
296            time INTEGER NOT NULL,
297            PRIMARY KEY (sid))',
298        'faqsessions_idx' => 'CREATE INDEX idx_time_%s ON %sfaqsessions (time)',
299        'faqsearches_searchterm_idx' => 'CREATE INDEX idx_faqsearches_searchterm_%s ON %sfaqsearches (searchterm)',
300        'faqsearches_date_term_idx' => 'CREATE INDEX idx_faqsearches_date_term_%s ON %sfaqsearches (searchdate, searchterm)',
301        'faqsearches_date_term_lang_idx' => 'CREATE INDEX idx_faqsearches_date_term_lang_%s ON %sfaqsearches (searchdate, searchterm, lang)',
302        'faqstopwords' => 'CREATE TABLE %sfaqstopwords (
303            id SERIAL NOT NULL,
304            lang VARCHAR(5) NOT NULL,
305            stopword VARCHAR(64) NOT NULL,
306            PRIMARY KEY (id, lang))',
307        'faqtags' => 'CREATE TABLE %sfaqtags (
308            tagging_id SERIAL NOT NULL,
309            tagging_name VARCHAR(255) NOT NULL ,
310            PRIMARY KEY (tagging_id, tagging_name))',
311        'faquser' => 'CREATE TABLE %sfaquser (
312            user_id SERIAL NOT NULL,
313            login VARCHAR(128) NOT NULL,
314            session_id VARCHAR(150) NULL,
315            session_timestamp INTEGER NULL,
316            ip VARCHAR(64) NULL,
317            account_status VARCHAR(50) NULL,
318            last_login VARCHAR(14) NULL,
319            auth_source VARCHAR(100) NULL,
320            member_since VARCHAR(14) NULL,
321            remember_me VARCHAR(150) NULL,
322            success SMALLINT NULL DEFAULT 1,
323            is_superadmin SMALLINT NULL DEFAULT 0,
324            login_attempts SMALLINT NULL DEFAULT 0,
325            refresh_token TEXT NULL DEFAULT NULL,
326            access_token TEXT NULL DEFAULT NULL,
327            code_verifier VARCHAR(255) NULL DEFAULT NULL,
328            jwt TEXT NULL DEFAULT NULL,
329            webauthnkeys TEXT NULL DEFAULT NULL,
330            PRIMARY KEY (user_id))',
331        'faquserdata' => 'CREATE TABLE %sfaquserdata (
332            user_id SERIAL NOT NULL,
333            last_modified VARCHAR(14) NULL,
334            display_name VARCHAR(128) NULL,
335            email VARCHAR(128) NULL,
336            keycloak_sub VARCHAR(255) NULL,
337            is_visible SMALLINT NULL DEFAULT 0,
338            twofactor_enabled SMALLINT NULL DEFAULT 0,
339            secret VARCHAR(128) NULL DEFAULT NULL)',
340        'faquserlogin' => 'CREATE TABLE %sfaquserlogin (
341            login VARCHAR(128) NOT NULL,
342            pass VARCHAR(80) NULL,
343            domain VARCHAR(255) NULL,
344            PRIMARY KEY (login))',
345        'faquser_group' => 'CREATE TABLE %sfaquser_group (
346            user_id INTEGER NOT NULL,
347            group_id INTEGER NOT NULL,
348            PRIMARY KEY (user_id, group_id))',
349        'faquser_right' => 'CREATE TABLE %sfaquser_right (
350            user_id INTEGER NOT NULL,
351            right_id INTEGER NOT NULL,
352            PRIMARY KEY (user_id, right_id))',
353        'faqvisits' => 'CREATE TABLE %sfaqvisits (
354            id SERIAL NOT NULL,
355            lang VARCHAR(5) NOT NULL,
356            visits INTEGER NOT NULL,
357            last_visit INTEGER NOT NULL,
358            PRIMARY KEY (id, lang))',
359        'faqvoting' => 'CREATE TABLE %sfaqvoting (
360            id SERIAL NOT NULL,
361            artikel INTEGER NOT NULL,
362            vote INTEGER NOT NULL,
363            usr INTEGER NOT NULL,
364            datum VARCHAR(20) DEFAULT \'\',
365            ip VARCHAR(15) DEFAULT \'\',
366            PRIMARY KEY (id))',
367        'faqchat_messages' => 'CREATE TABLE %sfaqchat_messages (
368            id SERIAL NOT NULL,
369            sender_id INTEGER NOT NULL,
370            recipient_id INTEGER NOT NULL,
371            message TEXT NOT NULL,
372            is_read SMALLINT NOT NULL DEFAULT 0,
373            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
374            PRIMARY KEY (id))',
375        'faqchat_messages_idx_sender' => 'CREATE INDEX idx_chat_sender_%s ON %sfaqchat_messages (sender_id)',
376        'faqchat_messages_idx_recipient' => 'CREATE INDEX idx_chat_recipient_%s ON %sfaqchat_messages (recipient_id)',
377        'faqchat_messages_idx_conversation' => 'CREATE INDEX idx_chat_conversation_%s ON %sfaqchat_messages (sender_id, recipient_id)',
378        'faqchat_messages_idx_created' => 'CREATE INDEX idx_chat_created_%s ON %sfaqchat_messages (created_at)',
379    ];
380
381    /**
382     * Constructor.
383     */
384    public function __construct(Configuration $configuration)
385    {
386        $this->configuration = $configuration;
387    }
388
389    /**
390     * Executes all CREATE TABLE and CREATE INDEX statements.
391     *
392     */
393    public function createTables(string $prefix = '', ?string $schema = null): bool
394    {
395        if ($schema !== null && $schema !== '') {
396            if (!preg_match('/^[A-Za-z0-9_]+$/', $schema)) {
397                return false;
398            }
399
400            $quotedSchema = sprintf('"%s"', str_replace('"', replace: '""', subject: $schema));
401
402            if (!$this->configuration->getDb()->query(sprintf('CREATE SCHEMA IF NOT EXISTS %s', $quotedSchema))) {
403                return false;
404            }
405
406            if (!$this->configuration->getDb()->query(sprintf('SET search_path TO %s', $quotedSchema))) {
407                return false;
408            }
409        }
410
411        foreach ($this->createTableStatements as $key => $stmt) {
412            $needsTwoPrefixes =
413                $key === 'idx_records'
414                || $key === 'faqsessions_idx'
415                || str_starts_with($key, 'faqsearches_')
416                || str_starts_with($key, 'faqchat_messages_idx_');
417
418            $result = $needsTwoPrefixes
419                ? $this->configuration->getDb()->query(sprintf($stmt, $prefix, $prefix))
420                : $this->configuration->getDb()->query(sprintf($stmt, $prefix));
421
422            if (!$result) {
423                return false;
424            }
425        }
426
427        return true;
428    }
429}