Subversion Repositories oidplus

Rev

Rev 1316 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
635 daniel-mar 1
<?php
2
 
3
/*
4
 * OIDplus 2.0
1086 daniel-mar 5
 * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft
635 daniel-mar 6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
1050 daniel-mar 20
namespace ViaThinkSoft\OIDplus;
635 daniel-mar 21
 
1086 daniel-mar 22
// phpcs:disable PSR1.Files.SideEffects
23
\defined('INSIDE_OIDPLUS') or die;
24
// phpcs:enable PSR1.Files.SideEffects
25
 
635 daniel-mar 26
class OIDplusDatabaseConnectionPgSql extends OIDplusDatabaseConnection {
1130 daniel-mar 27
        /**
28
         * @var mixed|null
29
         */
635 daniel-mar 30
        private $conn = null;
1130 daniel-mar 31
 
32
        /**
33
         * @var array
34
         */
635 daniel-mar 35
        private $already_prepared = array();
1130 daniel-mar 36
 
37
        /**
38
         * @var string|null
39
         */
635 daniel-mar 40
        private $last_error = null; // do the same like MySQL+PDO, just to be equal in the behavior
41
 
1116 daniel-mar 42
        /**
43
         * @param string $sql
44
         * @param array|null $prepared_args
45
         * @return OIDplusQueryResultPgSql
46
         * @throws OIDplusException
47
         */
48
        public function doQuery(string $sql, array $prepared_args=null): OIDplusQueryResult {
635 daniel-mar 49
                $this->last_error = null;
50
                if (is_null($prepared_args)) {
51
                        $res = @pg_query($this->conn, $sql);
52
 
53
                        if ($res === false) {
54
                                $this->last_error = pg_last_error($this->conn);
55
                                throw new OIDplusSQLException($sql, $this->error());
56
                        } else {
57
                                return new OIDplusQueryResultPgSql($res);
58
                        }
59
                } else {
60
                        // convert ? ? ? to $1 $2 $3
61
                        $sql = preg_replace_callback('@\\?@', function($found) {
62
                                static $i = 0;
63
                                $i++;
64
                                return '$'.$i;
786 daniel-mar 65
                        }, $sql, count($prepared_args));
635 daniel-mar 66
 
67
                        $prepare_name = 'OIDplus_ps_'.sha1($sql);
68
                        if (!in_array($prepare_name, $this->already_prepared)) {
69
                                $res = @pg_prepare($this->conn, $prepare_name, $sql);
70
                                if ($res === false) {
71
                                        $this->last_error = pg_last_error($this->conn);
72
                                        throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
73
                                }
74
                                $this->already_prepared[] = $prepare_name;
75
                        }
76
 
77
                        foreach ($prepared_args as &$value) {
78
                                if (is_bool($value)) $value = $value ? '1' : '0';
79
                        }
1316 daniel-mar 80
                        unset($value);
635 daniel-mar 81
 
82
                        $ps = pg_execute($this->conn, $prepare_name, $prepared_args);
83
                        if ($ps === false) {
84
                                $this->last_error = pg_last_error($this->conn);
85
                                throw new OIDplusSQLException($sql, $this->error());
86
                        }
87
                        return new OIDplusQueryResultPgSql($ps);
88
                }
89
        }
90
 
1116 daniel-mar 91
        /**
92
         * @return int
93
         */
1160 daniel-mar 94
        public function doInsertId(): int {
635 daniel-mar 95
                try {
96
                        return (int)$this->query('select lastval() as id')->fetch_object()->id;
1050 daniel-mar 97
                } catch (\Exception $e) {
635 daniel-mar 98
                        return 0;
99
                }
100
        }
101
 
1116 daniel-mar 102
        /**
103
         * @return string
104
         */
635 daniel-mar 105
        public function error(): string {
106
                $err = $this->last_error;
107
                if ($err == null) $err = '';
108
                return $err;
109
        }
110
 
1116 daniel-mar 111
        /**
112
         * @return void
113
         * @throws OIDplusConfigInitializationException
114
         * @throws OIDplusException
115
         */
635 daniel-mar 116
        protected function doConnect()/*: void*/ {
117
                if (!function_exists('pg_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','PostgreSQL'));
118
 
119
                // Try connecting to the database
120
                ob_start();
121
                $err = '';
122
                try {
123
                        $host     = OIDplus::baseConfig()->getValue('PGSQL_HOST',     'localhost:5432');
124
                        $username = OIDplus::baseConfig()->getValue('PGSQL_USERNAME', 'postgres');
125
                        $password = OIDplus::baseConfig()->getValue('PGSQL_PASSWORD', '');
126
                        $database = OIDplus::baseConfig()->getValue('PGSQL_DATABASE', 'oidplus');
814 daniel-mar 127
                        $socket   = OIDplus::baseConfig()->getValue('PGSQL_SOCKET',   '');
128
                        if ($socket != '') {
129
                                $hostname = $socket;
130
                                $port = '';
131
                        } else {
132
                                list($hostname, $port) = explode(':', "$host:5432");
133
                        }
134
 
135
                        $connection_string = array();
1262 daniel-mar 136
                        if ($hostname != '') $connection_string[] = "host='".str_replace("'", "\\'", $hostname)."'";
137
                        if ($username != '') $connection_string[] = "user='".str_replace("'", "\\'", $username)."'";
138
                        if ($password != '') $connection_string[] = "password='".str_replace("'", "\\'", $password)."'";
139
                        if ($port     != '') $connection_string[] = "port='".str_replace("'", "\\'", $port)."'";
140
                        if ($database != '') $connection_string[] = "dbname='".str_replace("'", "\\'", $database)."'";
814 daniel-mar 141
 
635 daniel-mar 142
                        // We need to use PGSQL_CONNECT_FORCE_NEW because we require two connectoins (for isolated log message queries)
814 daniel-mar 143
                        $this->conn = pg_connect(implode(' ', $connection_string), PGSQL_CONNECT_FORCE_NEW);
635 daniel-mar 144
                } finally {
145
                        # TODO: this does not seem to work?! (at least not for CLI)
146
                        $err = ob_get_contents();
147
                        ob_end_clean();
148
                }
149
 
150
                if (!$this->conn) {
863 daniel-mar 151
                        throw new OIDplusConfigInitializationException(trim(_L('Connection to the database failed!').' ' . strip_tags($err)));
635 daniel-mar 152
                }
153
 
154
                $this->already_prepared = array();
155
                $this->last_error = null;
156
 
157
                try {
158
                        $this->query("SET NAMES 'utf8'");
1050 daniel-mar 159
                } catch (\Exception $e) {
635 daniel-mar 160
                }
161
        }
162
 
1116 daniel-mar 163
        /**
164
         * @return void
165
         */
635 daniel-mar 166
        protected function doDisconnect()/*: void*/ {
167
                $this->already_prepared = array();
168
                if (!is_null($this->conn)) {
169
                        pg_close($this->conn);
170
                        $this->conn = null;
171
                }
172
        }
173
 
1116 daniel-mar 174
        /**
175
         * @var bool
176
         */
635 daniel-mar 177
        private $intransaction = false;
178
 
1116 daniel-mar 179
        /**
180
         * @return bool
181
         */
635 daniel-mar 182
        public function transaction_supported(): bool {
183
                return true;
184
        }
185
 
1116 daniel-mar 186
        /**
187
         * @return int
188
         */
635 daniel-mar 189
        public function transaction_level(): int {
190
                return $this->intransaction ? 1 : 0;
191
        }
192
 
1116 daniel-mar 193
        /**
194
         * @return void
195
         * @throws OIDplusException
196
         */
635 daniel-mar 197
        public function transaction_begin()/*: void*/ {
198
                if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
199
                $this->query('begin transaction');
200
                $this->intransaction = true;
201
        }
202
 
1116 daniel-mar 203
        /**
204
         * @return void
205
         * @throws OIDplusException
206
         */
635 daniel-mar 207
        public function transaction_commit()/*: void*/ {
208
                $this->query('commit');
209
                $this->intransaction = false;
210
        }
211
 
1116 daniel-mar 212
        /**
213
         * @return void
214
         * @throws OIDplusException
215
         */
635 daniel-mar 216
        public function transaction_rollback()/*: void*/ {
217
                $this->query('rollback');
218
                $this->intransaction = false;
219
        }
220
 
1116 daniel-mar 221
        /**
222
         * @return string
223
         */
635 daniel-mar 224
        public function sqlDate(): string {
225
                return 'now()';
226
        }
227
 
1116 daniel-mar 228
        /**
229
         * @param bool $mustExist
230
         * @return OIDplusSqlSlangPlugin|null
231
         * @throws OIDplusConfigInitializationException
232
         */
635 daniel-mar 233
        protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
234
                $slang = OIDplus::getSqlSlangPlugin('pgsql');
235
                if (is_null($slang)) {
1430 daniel-mar 236
                        throw new OIDplusConfigInitializationException(_L('SQL-Slang plugin "%1" is missing. Please check if it exists in the directory "plugin/sqlSlang". If it is not existing, please recover it from an GIT/SVN snapshot or OIDplus archive file.','pgsql'));
635 daniel-mar 237
                }
238
                return $slang;
239
        }
1220 daniel-mar 240
 
241
        /**
242
         * @return array
243
         */
244
        public function getExtendedInfo(): array {
245
                $host     = OIDplus::baseConfig()->getValue('PGSQL_HOST',     'localhost:5432');
246
                $username = OIDplus::baseConfig()->getValue('PGSQL_USERNAME', 'postgres');
247
                $password = OIDplus::baseConfig()->getValue('PGSQL_PASSWORD', '');
248
                $database = OIDplus::baseConfig()->getValue('PGSQL_DATABASE', 'oidplus');
249
                $socket   = OIDplus::baseConfig()->getValue('PGSQL_SOCKET',   '');
250
                if ($socket != '') {
251
                        $hostname = $socket;
252
                        $port = '';
253
                } else {
254
                        list($hostname, $port) = explode(':', "$host:5432");
255
                }
256
                return array(
257
                        _L('Hostname') => $hostname,
258
                        _L('Port') => $port,
259
                        _L('Socket') => $socket,
260
                        _L('Database') => $database,
261
                        _L('Username') => $username
262
                );
263
        }
264
 
635 daniel-mar 265
}