Test Failed
Push — master ( c97961...600310 )
by Joe
02:40
created

Db::lock()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 10
nc 3
nop 2
dl 0
loc 15
ccs 0
cts 6
cp 0
crap 20
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * PostgreSQL Related Functionality
4
 * @author Joe Huss <[email protected]>
5
 * @copyright 2018
6
 * @package MyAdmin
7
 * @category SQL
8
 */
9
10
namespace MyDb\Pgsql;
11
12
use \MyDb\Generic;
13
use \MyDb\Db_Interface;
14
15
/**
16
 * Db
17
 *
18
 * @access public
19
 */
20
class Db extends Generic implements Db_Interface {
21
	/* public: this is an api revision, not a CVS revision. */
22
	public $type = 'pgsql';
23
	public $port = '5432';
24
25
	/**
26
	 * Db::ifadd()
27
	 *
28
	 * @param mixed $add
29
	 * @param mixed $me
30
	 * @return string
31
	 */
32
	public function ifadd($add, $me) {
33
		if ('' != $add)
34
			return ' '.$me.$add;
35
		return '';
36
	}
37
38
	/**
39
	 * @param $string
40
	 * @return string
41
	 */
42
	public function real_escape($string = '') {
43
		return $this->escape($string);
44
	}
45
46
	/**
47
	 * alias function of select_db, changes the database we are working with.
48
	 *
49
	 * @param string $database the name of the database to use
50
	 * @return void
51
	 */
52
	public function useDb($database) {
53
		$this->selectDb($database);
54
	}
55
56
	/**
57
	 * changes the database we are working with.
58
	 *
59
	 * @param string $database the name of the database to use
60
	 * @return void
61
	 */
62
	public function selectDb($database) {
0 ignored issues
show
Unused Code introduced by
The parameter $database is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

62
	public function selectDb(/** @scrutinizer ignore-unused */ $database) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
63
		/*if ($database != $this->database) {
64
			$this->database = $database;
65
			$this->linkId = null;
66
			$this->connect();
67
		}*/
68
	}
69
70
	/**
71
	 * Db::connect()
72
	 * @return void
73
	 */
74
	public function connect() {
75
		if (0 == $this->linkId) {
76
			$connectString = 'dbname='.$this->database.$this->ifadd($this->host, 'host=').$this->ifadd($this->port, 'port=').$this->ifadd($this->user, 'user=').$this->ifadd("'".$this->password."'", 'password=');
77
			$this->linkId = pg_pconnect($connectString);
0 ignored issues
show
Documentation Bug introduced by
It seems like pg_pconnect($connectString) of type resource is incompatible with the declared type object|integer of property $linkId.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
78
79
			if (!$this->linkId) {
80
				$this->halt('Link-ID == FALSE, '.($GLOBALS['phpgw_info']['server']['db_persistent'] ? 'p' : '').'connect failed');
81
			}
82
		}
83
	}
84
85
	/* This only affects systems not using persistent connections */
86
87
	/**
88
	 * Db::disconnect()
89
	 * @return bool
90
	 */
91
	public function disconnect() {
92
		return @pg_close($this->linkId);
0 ignored issues
show
Bug introduced by
$this->linkId of type object|integer is incompatible with the type resource expected by parameter $connection of pg_close(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

92
		return @pg_close(/** @scrutinizer ignore-type */ $this->linkId);
Loading history...
93
	}
94
95
	/**
96
	 * Db::queryReturn()
97
	 *
98
	 * Sends an SQL query to the server like the normal query() command but iterates through
99
	 * any rows and returns the row or rows immediately or FALSE on error
100
	 *
101
	 * @param mixed $query SQL Query to be used
102
	 * @param string $line optionally pass __LINE__ calling the query for logging
103
	 * @param string $file optionally pass __FILE__ calling the query for logging
104
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
105
	 */
106
	public function queryReturn($query, $line = '', $file = '') {
107
		$this->query($query, $line, $file);
108
		if ($this->num_rows() == 0) {
109
			return false;
110
		} elseif ($this->num_rows() == 1) {
111
			$this->next_record(MYSQL_ASSOC);
0 ignored issues
show
introduced by
The constant MYSQL_ASSOC has been deprecated: 5.5 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

111
			$this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC);
Loading history...
112
			return $this->Record;
113
		} else {
114
			$out = [];
115
			while ($this->next_record(MYSQL_ASSOC))
0 ignored issues
show
introduced by
The constant MYSQL_ASSOC has been deprecated: 5.5 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

115
			while ($this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC))
Loading history...
116
				$out[] = $this->Record;
117
			return $out;
118
		}
119
	}
120
121
	/**
122
	 * db:qr()
123
	 *
124
	 *  alias of queryReturn()
125
	 *
126
	 * @param mixed $query SQL Query to be used
127
	 * @param string $line optionally pass __LINE__ calling the query for logging
128
	 * @param string $file optionally pass __FILE__ calling the query for logging
129
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
130
	 */
131
	public function qr($query, $line = '', $file = '') {
132
		return $this->queryReturn($query, $line, $file);
133
	}
134
135
	/**
136
	 * Db::query()
137
	 *
138
	 *  Sends an SQL query to the database
139
	 *
140
	 * @param mixed $queryString
141
	 * @param string $line
142
	 * @param string $file
143
	 * @return mixed 0 if no query or query id handler, safe to ignore this return
144
	 */
145
	public function query($queryString, $line = '', $file = '') {
146
		if (!$line && !$file) {
147
			if (isset($GLOBALS['tf']))
148
				$GLOBALS['tf']->warning(__LINE__, __FILE__, "Lazy developer didn't pass __LINE__ and __FILE__ to db->query() - Actually query: $queryString");
149
		}
150
151
		/* No empty queries, please, since PHP4 chokes on them. */
152
		/* The empty query string is passed on from the constructor,
153
		* when calling the class without a query, e.g. in situations
154
		* like these: '$db = new db_Subclass;'
155
		*/
156
		if ($queryString == '')
157
			return 0;
158
159
		$this->connect();
160
161
		/* printf("<br>Debug: query = %s<br>\n", $queryString); */
162
163
		$this->queryId = @pg_exec($this->linkId, $queryString);
164
		$this->Row = 0;
165
166
		$this->Error = pg_errormessage($this->linkId);
167
		$this->Errno = ($this->Error == '') ? 0 : 1;
168
		if (!$this->queryId)
169
			$this->halt('Invalid SQL: '.$queryString, $line, $file);
170
171
		return $this->queryId;
172
	}
173
174
	/**
175
	 * Db::free()
176
	 *
177
	 * @return void
178
	 */
179
	public function free() {
180
		@pg_freeresult($this->queryId);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for pg_freeresult(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

180
		/** @scrutinizer ignore-unhandled */ @pg_freeresult($this->queryId);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
181
		$this->queryId = 0;
182
	}
183
184
	/**
185
	 * Db::next_record()
186
	 * @param mixed $resultType
187
	 * @return bool
188
	 */
189
	public function next_record($resultType = PGSQL_BOTH) {
190
		$this->Record = @pg_fetch_array($this->queryId, $this->Row++, $resultType);
0 ignored issues
show
Bug introduced by
$this->queryId of type integer is incompatible with the type resource expected by parameter $result of pg_fetch_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
		$this->Record = @pg_fetch_array(/** @scrutinizer ignore-type */ $this->queryId, $this->Row++, $resultType);
Loading history...
191
192
		$this->Error = pg_errormessage($this->linkId);
193
		$this->Errno = ($this->Error == '') ? 0 : 1;
194
195
		$stat = is_array($this->Record);
196
		if (!$stat && $this->autoFree) {
0 ignored issues
show
introduced by
The condition $stat is always true.
Loading history...
197
			pg_freeresult($this->queryId);
198
			$this->queryId = 0;
199
		}
200
		return $stat;
201
	}
202
203
	/**
204
	 * Db::seek()
205
	 *
206
	 * @param mixed $pos
207
	 * @return void
208
	 */
209
	public function seek($pos) {
210
		$this->Row = $pos;
211
	}
212
213
	/**
214
	 * Db::transactionBegin()
215
	 *
216
	 * @return mixed
217
	 */
218
	public function transactionBegin() {
219
		return $this->query('begin');
220
	}
221
222
	/**
223
	 * Db::transactionCommit()
224
	 * @return bool|mixed
225
	 */
226
	public function transactionCommit() {
227
		if (!$this->Errno) {
228
			return pg_exec($this->linkId, 'commit');
229
		} else {
230
			return false;
231
		}
232
	}
233
234
	/**
235
	 * Db::transactionAbort()
236
	 * @return mixed
237
	 */
238
	public function transactionAbort() {
239
		return pg_exec($this->linkId, 'rollback');
240
	}
241
242
	/**
243
	 * Db::getLastInsertId()
244
	 * @param mixed $table
245
	 * @param mixed $field
246
	 * @return int
247
	 */
248
	public function getLastInsertId($table, $field) {
249
		/* This will get the last insert ID created on the current connection.  Should only be called
250
		* after an insert query is run on a table that has an auto incrementing field.  Of note, table
251
		* and field are required because pgsql returns the last inserted OID, which is unique across
252
		* an entire installation.  These params allow us to retrieve the sequenced field without adding
253
		* conditional code to the apps.
254
		*/
255
		if (!isset($table) || $table == '' || !isset($field) || $field == '')
256
			return -1;
257
258
		$oid = pg_getlastoid($this->queryId);
259
		if ($oid == -1)
260
			return -1;
261
262
		$result = @pg_exec($this->linkId, "select $field from $table where oid=$oid");
263
		if (!$result)
264
			return -1;
265
266
		$Record = @pg_fetch_array($result, 0);
267
		@pg_freeresult($result);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for pg_freeresult(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

267
		/** @scrutinizer ignore-unhandled */ @pg_freeresult($result);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
268
		if (!is_array($Record)) /* OID not found? */
0 ignored issues
show
introduced by
The condition is_array($Record) is always true.
Loading history...
269
		{
270
			return -1;
271
		}
272
273
		return $Record[0];
274
	}
275
276
	/**
277
	 * Db::lock()
278
	 * @param mixed  $table
279
	 * @param string $mode
280
	 * @return int|mixed
281
	 */
282
	public function lock($table, $mode = 'write') {
283
		$result = $this->transactionBegin();
284
285
		if ($mode == 'write') {
286
			if (is_array($table)) {
287
				foreach ($table as $t)
288
					$result = pg_exec($this->linkId, 'lock table '.$t[1].' in share mode');
289
			} else {
290
				$result = pg_exec($this->linkId, 'lock table '.$table.' in share mode');
291
			}
292
		} else {
293
			$result = 1;
294
		}
295
296
		return $result;
297
	}
298
299
	/**
300
	 * Db::unlock()
301
	 * @return bool|mixed
302
	 */
303
	public function unlock() {
304
		return $this->transactionCommit();
305
	}
306
307
	/**
308
	 * Db::affectedRows()
309
	 * @return void
310
	 */
311
	public function affectedRows() {
312
		return pg_cmdtuples($this->queryId);
313
	}
314
315
	/**
316
	 * Db::num_rows()
317
	 * @return int
318
	 */
319
	public function num_rows() {
320
		return pg_numrows($this->queryId);
321
	}
322
323
	/**
324
	 * Db::num_fields()
325
	 * @return int
326
	 */
327
	public function num_fields() {
328
		return pg_numfields($this->queryId);
329
	}
330
331
	/**
332
	 * @param mixed $msg
333
	 * @param string $line
334
	 * @param string $file
335
	 * @return mixed|void
336
	 */
337
	public function haltmsg($msg, $line = '', $file = '') {
338
		$this->log("Database error: $msg", $line, $file, 'error');
339
		if ($this->Errno != '0' || $this->Error != '()')
340
			$this->log('PostgreSQL Error: '.pg_last_error($this->linkId), $line, $file, 'error');
0 ignored issues
show
Bug introduced by
$this->linkId of type object|integer is incompatible with the type resource expected by parameter $connection of pg_last_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

340
			$this->log('PostgreSQL Error: '.pg_last_error(/** @scrutinizer ignore-type */ $this->linkId), $line, $file, 'error');
Loading history...
341
		$this->logBackTrace($msg, $line, $file);
342
	}
343
344
	/**
345
	 * Db::tableNames()
346
	 *
347
	 * @return array
348
	 */
349
	public function tableNames() {
350
		$return = [];
351
		$this->query("select relname from pg_class where relkind = 'r' and not relname like 'pg_%'");
352
		$i = 0;
353
		while ($this->next_record()) {
354
			$return[$i]['table_name'] = $this->f(0);
355
			$return[$i]['tablespace_name'] = $this->database;
356
			$return[$i]['database'] = $this->database;
357
			++$i;
358
		}
359
		return $return;
360
	}
361
362
	/**
363
	 * Db::indexNames()
364
	 *
365
	 * @return array
366
	 */
367
	public function indexNames() {
368
		$return = [];
369
		$this->query("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relkind ='i' ORDER BY relname");
370
		$i = 0;
371
		while ($this->next_record()) {
372
			$return[$i]['index_name'] = $this->f(0);
373
			$return[$i]['tablespace_name'] = $this->database;
374
			$return[$i]['database'] = $this->database;
375
			++$i;
376
		}
377
		return $return;
378
	}
379
}
380