Completed
Push — master ( 26c785...3bf766 )
by Joe
06:06
created

Db::unlock()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * PostgreSQL Related Functionality
4
 * @author Joe Huss <[email protected]>
5
 * @copyright 2019
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
{
22
	/* public: this is an api revision, not a CVS revision. */
23
	public $type = 'pgsql';
24
	public $port = '';
25
	public $defaultPort = '5432';
26
	
27
28
	/**
29
	 * adds if not blank
30
	 *
31
	 * @param string $add the value to set
32
	 * @param string $me the key/field to set the value for
33
	 * @param false|string $quote optional indicate the value needs quoted
34
	 * @return string
35
	 */
36
	public function ifadd($add, $me, $quote = false)
37
	{
38
		if ('' != $add) {
39
			return ' '.$me.($quote === false ? '' : $quote).$add.($quote === false ? '' : $quote);
40
		}
41
		return '';
42
	}
43
44
	/**
45
	 * @param $string
46
	 * @return string
47
	 */
48
	public function real_escape($string = '')
49
	{
50
		return $this->escape($string);
51
	}
52
53
	/**
54
	 * alias function of select_db, changes the database we are working with.
55
	 *
56
	 * @param string $database the name of the database to use
57
	 * @return void
58
	 */
59
	public function useDb($database)
60
	{
61
		$this->selectDb($database);
62
	}
63
64
	/**
65
	 * changes the database we are working with.
66
	 *
67
	 * @param string $database the name of the database to use
68
	 * @return void
69
	 */
70
	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

70
	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...
71
	{
72
		/*if ($database != $this->database) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
73
			$this->database = $database;
74
			$this->linkId = null;
75
			$this->connect();
76
		}*/
77
	}
78
79
	/**
80
	 * Db::connect()
81
	 * @return void
82
	 */
83
	public function connect()
84
	{
85
		if (0 == $this->linkId) {
86
			$connectString = trim($this->ifadd($this->host, 'host=').
87
							 $this->ifadd($this->port, 'port=').
88
							 $this->ifadd($this->database, 'dbname=').
89
							 $this->ifadd($this->user, 'user=').
90
							 $this->ifadd($this->password, 'password=', "'"));
91
			$this->linkId = pg_connect($connectString);
0 ignored issues
show
Documentation Bug introduced by
It seems like pg_connect($connectString) of type resource is incompatible with the declared type integer|object 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...
92
			if (!$this->linkId) {
93
				$this->halt('Link-ID == FALSE, connect failed');
94
			}
95
		}
96
	}
97
98
	/* This only affects systems not using persistent connections */
99
100
	/**
101
	 * Db::disconnect()
102
	 * @return bool
103
	 */
104
	public function disconnect()
105
	{
106
		return @pg_close($this->linkId);
0 ignored issues
show
Bug introduced by
$this->linkId of type integer|object 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

106
		return @pg_close(/** @scrutinizer ignore-type */ $this->linkId);
Loading history...
107
	}
108
109
	/**
110
	 * Db::queryReturn()
111
	 *
112
	 * Sends an SQL query to the server like the normal query() command but iterates through
113
	 * any rows and returns the row or rows immediately or FALSE on error
114
	 *
115
	 * @param mixed $query SQL Query to be used
116
	 * @param string $line optionally pass __LINE__ calling the query for logging
117
	 * @param string $file optionally pass __FILE__ calling the query for logging
118
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
119
	 */
120
	public function queryReturn($query, $line = '', $file = '')
121
	{
122
		$this->query($query, $line, $file);
123
		if ($this->num_rows() == 0) {
124
			return false;
125
		} elseif ($this->num_rows() == 1) {
126
			$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

126
			$this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC);
Loading history...
127
			return $this->Record;
128
		} else {
129
			$out = [];
130
			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

130
			while ($this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC)) {
Loading history...
131
				$out[] = $this->Record;
132
			}
133
			return $out;
134
		}
135
	}
136
137
	/**
138
	 * db:qr()
139
	 *
140
	 *  alias of queryReturn()
141
	 *
142
	 * @param mixed $query SQL Query to be used
143
	 * @param string $line optionally pass __LINE__ calling the query for logging
144
	 * @param string $file optionally pass __FILE__ calling the query for logging
145
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
146
	 */
147
	public function qr($query, $line = '', $file = '')
148
	{
149
		return $this->queryReturn($query, $line, $file);
150
	}
151
152
	/**
153
	 * Db::query()
154
	 *
155
	 *  Sends an SQL query to the database
156
	 *
157
	 * @param mixed $queryString
158
	 * @param string $line
159
	 * @param string $file
160
	 * @return mixed 0 if no query or query id handler, safe to ignore this return
161
	 */
162
	public function query($queryString, $line = '', $file = '')
163
	{
164
		/* No empty queries, please, since PHP4 chokes on them. */
165
		/* The empty query string is passed on from the constructor,
166
		* when calling the class without a query, e.g. in situations
167
		* like these: '$db = new db_Subclass;'
168
		*/
169
		if ($queryString == '') {
170
			return 0;
171
		}
172
173
		$this->connect();
174
175
		/* printf("<br>Debug: query = %s<br>\n", $queryString); */
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
176
177
		$this->queryId = @pg_exec($this->linkId, $queryString);
178
		$this->Row = 0;
179
180
		$this->Error = pg_errormessage($this->linkId);
181
		$this->Errno = ($this->Error == '') ? 0 : 1;
182
		if (!$this->queryId) {
183
			$this->halt('Invalid SQL: '.$queryString, $line, $file);
184
		}
185
186
		return $this->queryId;
187
	}
188
189
	/**
190
	 * Db::free()
191
	 *
192
	 * @return void
193
	 */
194
	public function free()
195
	{
196
		@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

196
		/** @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...
197
		$this->queryId = 0;
198
	}
199
200
	/**
201
	 * Db::next_record()
202
	 * @param mixed $resultType
203
	 * @return bool
204
	 */
205
	public function next_record($resultType = PGSQL_BOTH)
206
	{
207
		$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

207
		$this->Record = @pg_fetch_array(/** @scrutinizer ignore-type */ $this->queryId, $this->Row++, $resultType);
Loading history...
208
209
		$this->Error = pg_errormessage($this->linkId);
210
		$this->Errno = ($this->Error == '') ? 0 : 1;
211
212
		$stat = is_array($this->Record);
213
		if (!$stat && $this->autoFree) {
0 ignored issues
show
introduced by
The condition $stat is always true.
Loading history...
214
			pg_freeresult($this->queryId);
215
			$this->queryId = 0;
216
		}
217
		return $stat;
218
	}
219
220
	/**
221
	 * Db::seek()
222
	 *
223
	 * @param mixed $pos
224
	 * @return void
225
	 */
226
	public function seek($pos)
227
	{
228
		$this->Row = $pos;
229
	}
230
231
	/**
232
	 * Db::transactionBegin()
233
	 *
234
	 * @return mixed
235
	 */
236
	public function transactionBegin()
237
	{
238
		return $this->query('begin');
239
	}
240
241
	/**
242
	 * Db::transactionCommit()
243
	 * @return bool|mixed
244
	 */
245
	public function transactionCommit()
246
	{
247
		if (!$this->Errno) {
248
			return pg_exec($this->linkId, 'commit');
249
		} else {
250
			return false;
251
		}
252
	}
253
254
	/**
255
	 * Db::transactionAbort()
256
	 * @return mixed
257
	 */
258
	public function transactionAbort()
259
	{
260
		return pg_exec($this->linkId, 'rollback');
261
	}
262
263
	/**
264
	 * Db::getLastInsertId()
265
	 * @param mixed $table
266
	 * @param mixed $field
267
	 * @return int
268
	 */
269
	public function getLastInsertId($table, $field)
270
	{
271
		/* This will get the last insert ID created on the current connection.  Should only be called
272
		* after an insert query is run on a table that has an auto incrementing field.  Of note, table
273
		* and field are required because pgsql returns the last inserted OID, which is unique across
274
		* an entire installation.  These params allow us to retrieve the sequenced field without adding
275
		* conditional code to the apps.
276
		*/
277
		if (!isset($table) || $table == '' || !isset($field) || $field == '') {
278
			return -1;
279
		}
280
281
		$oid = pg_getlastoid($this->queryId);
282
		if ($oid == -1) {
283
			return -1;
284
		}
285
286
		$result = @pg_exec($this->linkId, "select $field from $table where oid=$oid");
287
		if (!$result) {
288
			return -1;
289
		}
290
291
		$Record = @pg_fetch_array($result, 0);
292
		@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

292
		/** @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...
293
		if (!is_array($Record)) /* OID not found? */
0 ignored issues
show
introduced by
The condition is_array($Record) is always true.
Loading history...
294
		{
295
			return -1;
296
		}
297
298
		return $Record[0];
299
	}
300
301
	/**
302
	 * Db::lock()
303
	 * @param mixed  $table
304
	 * @param string $mode
305
	 * @return int|mixed
306
	 */
307
	public function lock($table, $mode = 'write')
308
	{
309
		$result = $this->transactionBegin();
310
311
		if ($mode == 'write') {
312
			if (is_array($table)) {
313
				foreach ($table as $t) {
314
					$result = pg_exec($this->linkId, 'lock table '.$t[1].' in share mode');
315
				}
316
			} else {
317
				$result = pg_exec($this->linkId, 'lock table '.$table.' in share mode');
318
			}
319
		} else {
320
			$result = 1;
321
		}
322
323
		return $result;
324
	}
325
326
	/**
327
	 * Db::unlock()
328
	 * @return bool|mixed
329
	 */
330
	public function unlock()
331
	{
332
		return $this->transactionCommit();
333
	}
334
335
	/**
336
	 * Db::affectedRows()
337
	 * @return void
338
	 */
339
	public function affectedRows()
340
	{
341
		return pg_cmdtuples($this->queryId);
342
	}
343
344
	/**
345
	 * Db::num_rows()
346
	 * @return int
347
	 */
348
	public function num_rows()
349
	{
350
		return pg_numrows($this->queryId);
351
	}
352
353
	/**
354
	 * Db::num_fields()
355
	 * @return int
356
	 */
357
	public function num_fields()
358
	{
359
		return pg_numfields($this->queryId);
360
	}
361
362
	/**
363
	 * @param mixed $msg
364
	 * @param string $line
365
	 * @param string $file
366
	 * @return mixed|void
367
	 */
368
	public function haltmsg($msg, $line = '', $file = '')
369
	{
370
		$this->log("Database error: $msg", $line, $file, 'error');
371
		if ($this->Errno != '0' || $this->Error != '()') {
372
			$this->log('PostgreSQL Error: '.pg_last_error($this->linkId), $line, $file, 'error');
0 ignored issues
show
Bug introduced by
$this->linkId of type integer|object 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

372
			$this->log('PostgreSQL Error: '.pg_last_error(/** @scrutinizer ignore-type */ $this->linkId), $line, $file, 'error');
Loading history...
373
		}
374
		$this->logBackTrace($msg, $line, $file);
375
	}
376
377
	/**
378
	 * Db::tableNames()
379
	 *
380
	 * @return array
381
	 */
382
	public function tableNames()
383
	{
384
		$return = [];
385
		$this->query("select relname from pg_class where relkind = 'r' and not relname like 'pg_%'");
386
		$i = 0;
387
		while ($this->next_record()) {
388
			$return[$i]['table_name'] = $this->f(0);
389
			$return[$i]['tablespace_name'] = $this->database;
390
			$return[$i]['database'] = $this->database;
391
			++$i;
392
		}
393
		return $return;
394
	}
395
396
	/**
397
	 * Db::indexNames()
398
	 *
399
	 * @return array
400
	 */
401
	public function indexNames()
402
	{
403
		$return = [];
404
		$this->query("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relkind ='i' ORDER BY relname");
405
		$i = 0;
406
		while ($this->next_record()) {
407
			$return[$i]['index_name'] = $this->f(0);
408
			$return[$i]['tablespace_name'] = $this->database;
409
			$return[$i]['database'] = $this->database;
410
			++$i;
411
		}
412
		return $return;
413
	}
414
}
415