Subversion Repositories oidplus

Rev

Rev 863 | Rev 1086 | Go to most recent revision | 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
5
 * Copyright 2019 - 2021 Daniel Marschall, ViaThinkSoft
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
 
22
class OIDplusDatabaseConnectionPgSql extends OIDplusDatabaseConnection {
23
        private $conn = null;
24
        private $already_prepared = array();
25
        private $last_error = null; // do the same like MySQL+PDO, just to be equal in the behavior
26
 
27
        public function doQuery(string $sql, /*?array*/ $prepared_args=null): OIDplusQueryResult {
28
                $this->last_error = null;
29
                if (is_null($prepared_args)) {
30
                        $res = @pg_query($this->conn, $sql);
31
 
32
                        if ($res === false) {
33
                                $this->last_error = pg_last_error($this->conn);
34
                                throw new OIDplusSQLException($sql, $this->error());
35
                        } else {
36
                                return new OIDplusQueryResultPgSql($res);
37
                        }
38
                } else {
39
                        if (!is_array($prepared_args)) {
40
                                throw new OIDplusException(_L('"prepared_args" must be either NULL or an ARRAY.'));
41
                        }
42
 
43
                        // convert ? ? ? to $1 $2 $3
44
                        $sql = preg_replace_callback('@\\?@', function($found) {
45
                                static $i = 0;
46
                                $i++;
47
                                return '$'.$i;
786 daniel-mar 48
                        }, $sql, count($prepared_args));
635 daniel-mar 49
 
50
                        $prepare_name = 'OIDplus_ps_'.sha1($sql);
51
                        if (!in_array($prepare_name, $this->already_prepared)) {
52
                                $res = @pg_prepare($this->conn, $prepare_name, $sql);
53
                                if ($res === false) {
54
                                        $this->last_error = pg_last_error($this->conn);
55
                                        throw new OIDplusSQLException($sql, _L('Cannot prepare statement').': '.$this->error());
56
                                }
57
                                $this->already_prepared[] = $prepare_name;
58
                        }
59
 
60
                        foreach ($prepared_args as &$value) {
61
                                if (is_bool($value)) $value = $value ? '1' : '0';
62
                        }
63
 
64
                        $ps = pg_execute($this->conn, $prepare_name, $prepared_args);
65
                        if ($ps === false) {
66
                                $this->last_error = pg_last_error($this->conn);
67
                                throw new OIDplusSQLException($sql, $this->error());
68
                        }
69
                        return new OIDplusQueryResultPgSql($ps);
70
                }
71
        }
72
 
73
        public function insert_id(): int {
74
                try {
75
                        return (int)$this->query('select lastval() as id')->fetch_object()->id;
1050 daniel-mar 76
                } catch (\Exception $e) {
635 daniel-mar 77
                        return 0;
78
                }
79
        }
80
 
81
        public function error(): string {
82
                $err = $this->last_error;
83
                if ($err == null) $err = '';
84
                return $err;
85
        }
86
 
87
        protected function doConnect()/*: void*/ {
88
                if (!function_exists('pg_connect')) throw new OIDplusConfigInitializationException(_L('PHP extension "%1" not installed','PostgreSQL'));
89
 
90
                // Try connecting to the database
91
                ob_start();
92
                $err = '';
93
                try {
94
                        $host     = OIDplus::baseConfig()->getValue('PGSQL_HOST',     'localhost:5432');
95
                        $username = OIDplus::baseConfig()->getValue('PGSQL_USERNAME', 'postgres');
96
                        $password = OIDplus::baseConfig()->getValue('PGSQL_PASSWORD', '');
97
                        $database = OIDplus::baseConfig()->getValue('PGSQL_DATABASE', 'oidplus');
814 daniel-mar 98
                        $socket   = OIDplus::baseConfig()->getValue('PGSQL_SOCKET',   '');
99
                        if ($socket != '') {
100
                                $hostname = $socket;
101
                                $port = '';
102
                        } else {
103
                                list($hostname, $port) = explode(':', "$host:5432");
104
                        }
105
 
106
                        $connection_string = array();
107
                        if ($hostname != '') $connection_string[] = "host=$hostname";
108
                        if ($username != '') $connection_string[] = "user=$username";
109
                        if ($password != '') $connection_string[] = "password=$password";
110
                        if ($port     != '') $connection_string[] = "port=$port";
111
                        if ($database != '') $connection_string[] = "dbname=$database";
112
 
635 daniel-mar 113
                        // We need to use PGSQL_CONNECT_FORCE_NEW because we require two connectoins (for isolated log message queries)
814 daniel-mar 114
                        $this->conn = pg_connect(implode(' ', $connection_string), PGSQL_CONNECT_FORCE_NEW);
635 daniel-mar 115
                } finally {
116
                        # TODO: this does not seem to work?! (at least not for CLI)
117
                        $err = ob_get_contents();
118
                        ob_end_clean();
119
                }
120
 
121
                if (!$this->conn) {
863 daniel-mar 122
                        throw new OIDplusConfigInitializationException(trim(_L('Connection to the database failed!').' ' . strip_tags($err)));
635 daniel-mar 123
                }
124
 
125
                $this->already_prepared = array();
126
                $this->last_error = null;
127
 
128
                try {
129
                        $this->query("SET NAMES 'utf8'");
1050 daniel-mar 130
                } catch (\Exception $e) {
635 daniel-mar 131
                }
132
        }
133
 
134
        protected function doDisconnect()/*: void*/ {
135
                $this->already_prepared = array();
136
                if (!is_null($this->conn)) {
137
                        pg_close($this->conn);
138
                        $this->conn = null;
139
                }
140
        }
141
 
142
        private $intransaction = false;
143
 
144
        public function transaction_supported(): bool {
145
                return true;
146
        }
147
 
148
        public function transaction_level(): int {
149
                return $this->intransaction ? 1 : 0;
150
        }
151
 
152
        public function transaction_begin()/*: void*/ {
153
                if ($this->intransaction) throw new OIDplusException(_L('Nested transactions are not supported by this database plugin.'));
154
                $this->query('begin transaction');
155
                $this->intransaction = true;
156
        }
157
 
158
        public function transaction_commit()/*: void*/ {
159
                $this->query('commit');
160
                $this->intransaction = false;
161
        }
162
 
163
        public function transaction_rollback()/*: void*/ {
164
                $this->query('rollback');
165
                $this->intransaction = false;
166
        }
167
 
168
        public function sqlDate(): string {
169
                return 'now()';
170
        }
171
 
172
        protected function doGetSlang(bool $mustExist=true)/*: ?OIDplusSqlSlangPlugin*/ {
173
                $slang = OIDplus::getSqlSlangPlugin('pgsql');
174
                if (is_null($slang)) {
175
                        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 SVN snapshot or OIDplus TAR.GZ file.','pgsql'));
176
                }
177
                return $slang;
178
        }
179
}