Completed
Pull Request — patch_1-1-4 (#3202)
by Spuds
15:49
created

Database_PostgreSQL::data_seek()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
ccs 0
cts 6
cp 0
crap 2
1
<?php
2
3
/**
4
 * This file has all the main functions in it that relate to the Postgre database.
5
 *
6
 * @name      ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
9
 *
10
 * This file contains code covered by:
11
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
12
 * license:  	BSD, See included LICENSE.TXT for terms and conditions.
13
 *
14
 * @version 1.1
15
 *
16
 */
17
18
// Let's define the name of the class so that we will be able to use it in the instantiations
19
if (!defined('DB_TYPE'))
20
	define('DB_TYPE', 'PostgreSQL');
21
22
/**
23
 * PostgreSQL database class, implements database class to control mysql functions
24
 */
25
class Database_PostgreSQL extends Database_Abstract
26
{
27
	/**
28
	 * Holds current instance of the class
29
	 * @var Database_PostgreSQL
30
	 */
31
	private static $_db = null;
32
33
	/**
34
	 * Holds last query result
35
	 * @var string
36
	 */
37
	private $_db_last_result = null;
38
39
	/**
40
	 * Since PostgreSQL doesn't support INSERT REPLACE we are using this to remember
41
	 * the rows affected by the delete
42
	 * @var int
43
	 */
44
	private $_db_replace_result = null;
45
46
	/**
47
	 * A variable to remember if a transaction was started already or not
48
	 * @var boolean
49
	 */
50
	private $_in_transaction = false;
51
52
	/**
53
	 * Initializes a database connection.
54
	 * It returns the connection, if successful.
55
	 *
56
	 * @param string $db_server
57
	 * @param string $db_name
58
	 * @param string $db_user
59
	 * @param string $db_passwd
60
	 * @param string $db_prefix
61
	 * @param mixed[] $db_options
62
	 *
63
	 * @return resource
64
	 * @throws Elk_Exception
65
	 */
66
	public static function initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array())
0 ignored issues
show
Unused Code introduced by
The parameter $db_prefix is not used and could be removed.

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

Loading history...
67
	{
68
		// initialize the instance... if not done already!
69
		if (self::$_db === null)
70
			self::$_db = new self();
71
72 View Code Duplication
		if (!empty($db_options['port']))
73
			$db_port = ' port=' . (int) $db_options['port'];
74
		else
75
			$db_port = '';
76
77
		if (!empty($db_options['persist']))
78
			$connection = @pg_pconnect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
79
		else
80
			$connection = @pg_connect('host=' . $db_server . $db_port . ' dbname=' . $db_name . ' user=\'' . $db_user . '\' password=\'' . $db_passwd . '\'');
81
82
		// Something's wrong, show an error if its fatal (which we assume it is)
83 View Code Duplication
		if (!$connection)
84
		{
85
			if (!empty($db_options['non_fatal']))
86
				return null;
87
			else
88
				Errors::instance()->display_db_error();
89
		}
90
91
		self::$_db->_connection = $connection;
92
93
		return $connection;
94
	}
95
96
	/**
97
	 * Fix the database prefix if necessary.
98
	 * Do nothing on postgreSQL
99
	 *
100
	 * @param string $db_prefix
101
	 * @param string $db_name
102
	 *
103
	 * @return string
104
	 */
105
	public function fix_prefix($db_prefix, $db_name)
106
	{
107
		return $db_prefix;
108
	}
109
110
	/**
111
	 * Do a query.  Takes care of errors too.
112
	 * Special queries may need additional replacements to be appropriate
113
	 * for PostgreSQL.
114
	 *
115
	 * @param string $identifier
116
	 * @param string $db_string
117
	 * @param mixed[] $db_values
118
	 * @param resource|null $connection
119
	 *
120
	 * @return bool|resource|string
121
	 * @throws Elk_Exception
122
	 */
123
	public function query($identifier, $db_string, $db_values = array(), $connection = null)
124
	{
125
		global $db_show_debug, $time_start, $modSettings;
126
127
		// Decide which connection to use.
128
		$connection = $connection === null ? $this->_connection : $connection;
129
130
		// Special queries that need processing.
131
		$replacements = array(
132
			'alter_table' => array(
133
				'~(.+)~' => '',
134
			),
135
			'ban_suggest_error_ips' => array(
136
				'~RLIKE~' => '~',
137
				'~\\.~' => '\.',
138
			),
139
			'ban_suggest_message_ips' => array(
140
				'~RLIKE~' => '~',
141
				'~\\.~' => '\.',
142
			),
143
			'consolidate_spider_stats' => array(
144
				'~MONTH\(log_time\), DAYOFMONTH\(log_time\)~' => 'MONTH(CAST(CAST(log_time AS abstime) AS timestamp)), DAYOFMONTH(CAST(CAST(log_time AS abstime) AS timestamp))',
145
			),
146
			'display_get_post_poster' => array(
147
				'~GROUP BY id_msg\s+HAVING~' => 'AND',
148
			),
149
			'attach_download_increase' => array(
150
				'~LOW_PRIORITY~' => '',
151
			),
152
			'boardindex_fetch_boards' => array(
153
				'~COALESCE\(lb.id_msg, 0\) >= b.id_msg_updated~' => 'CASE WHEN COALESCE(lb.id_msg, 0) >= b.id_msg_updated THEN 1 ELSE 0 END',
154
			),
155
			'get_random_number' => array(
156
				'~RAND~' => 'RANDOM',
157
			),
158
			'insert_log_search_topics' => array(
159
				'~NOT RLIKE~' => '!~',
160
			),
161
			'insert_log_search_results_no_index' => array(
162
				'~NOT RLIKE~' => '!~',
163
			),
164
			'insert_log_search_results_subject' => array(
165
				'~NOT RLIKE~' => '!~',
166
			),
167
			'pm_conversation_list' => array(
168
				'~ORDER\\s+BY\\s+\\{raw:sort\\}~' => 'ORDER BY ' . (isset($db_values['sort']) ? ($db_values['sort'] === 'pm.id_pm' ? 'MAX(pm.id_pm)' : $db_values['sort']) : ''),
169
			),
170
			'top_topic_starters' => array(
171
				'~ORDER BY FIND_IN_SET\(id_member,(.+?)\)~' => 'ORDER BY STRPOS(\',\' || $1 || \',\', \',\' || id_member|| \',\')',
172
			),
173
			'unread_replies' => array(
174
				'~SELECT\\s+DISTINCT\\s+t.id_topic~' => 'SELECT t.id_topic, {raw:sort}',
175
			),
176
			'profile_board_stats' => array(
177
				'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
178
			),
179
		);
180
181
		if (isset($replacements[$identifier]))
182
			$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
183
184
		// Limits need to be a little different.
185
		$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
186
187
		if (trim($db_string) == '')
188
			return false;
189
190
		// Comments that are allowed in a query are preg_removed.
191
		static $allowed_comments_from = array(
192
			'~\s+~s',
193
			'~/\*!40001 SQL_NO_CACHE \*/~',
194
			'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
195
			'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
196
		);
197
		static $allowed_comments_to = array(
198
			' ',
199
			'',
200
			'',
201
			'',
202
		);
203
204
		// One more query....
205
		$this->_query_count++;
206
		$this->_db_replace_result = null;
207
208 View Code Duplication
		if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
209
			$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
210
211 View Code Duplication
		if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
212
		{
213
			// Store these values for use in the callback function.
214
			$this->_db_callback_values = $db_values;
215
			$this->_db_callback_connection = $connection;
216
217
			// Inject the values passed to this function.
218
			$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string);
219
220
			// No need for them any longer.
221
			$this->_db_callback_values = array();
222
			$this->_db_callback_connection = null;
223
		}
224
225
		// Debugging.
226 View Code Duplication
		if ($db_show_debug === true)
227
		{
228
			$debug = Debug::instance();
229
230
			// Get the file and line number this function was called.
231
			list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
232
233
			if (!empty($_SESSION['debug_redirect']))
234
			{
235
				$debug->merge_db($_SESSION['debug_redirect']);
236
				// @todo this may be off by 1
237
				$this->_query_count += count($_SESSION['debug_redirect']);
238
				$_SESSION['debug_redirect'] = array();
239
			}
240
241
			// Don't overload it.
242
			$st = microtime(true);
243
			$db_cache = array();
244
			$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...';
245
			$db_cache['f'] = $file;
246
			$db_cache['l'] = $line;
247
			$db_cache['s'] = $st - $time_start;
248
		}
249
250
		// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
251
		if (empty($modSettings['disableQueryCheck']))
252
		{
253
			$clean = '';
254
			$old_pos = 0;
255
			$pos = -1;
256 View Code Duplication
			while (true)
257
			{
258
				$pos = strpos($db_string, '\'', $pos + 1);
259
				if ($pos === false)
260
					break;
261
				$clean .= substr($db_string, $old_pos, $pos - $old_pos);
262
263
				while (true)
264
				{
265
					$pos1 = strpos($db_string, '\'', $pos + 1);
266
					$pos2 = strpos($db_string, '\'\'', $pos + 1);
267
268
					if ($pos1 === false)
269
						break;
270
					elseif ($pos2 === false || $pos2 > $pos1)
271
					{
272
						$pos = $pos1;
273
						break;
274
					}
275
276
					$pos = $pos2 + 1;
277
				}
278
279
				$clean .= ' %s ';
280
				$old_pos = $pos + 1;
281
			}
282
283
			$clean .= substr($db_string, $old_pos);
284
			$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
285
286
			// Comments?  We don't use comments in our queries, we leave 'em outside!
287 View Code Duplication
			if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
288
				$fail = true;
289
			// Trying to change passwords, slow us down, or something?
290
			elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
291
				$fail = true;
292
			elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
293
				$fail = true;
294
295
			if (!empty($fail) && class_exists('Errors'))
296
				$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
297
298
			// If we are updating something, better start a transaction so that indexes may be kept consistent
299
			if (!$this->_in_transaction && strpos($clean, 'update') !== false)
300
				$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
301
		}
302
303
		$this->_db_last_result = @pg_query($connection, $db_string);
0 ignored issues
show
Security SQL Injection introduced by
$db_string can contain request data and is used in sql context(s) leading to a potential security vulnerability.

13 paths for user data to reach this point

  1. Path: Read from $_POST, and $user_info is assigned in sources/controllers/Post.controller.php on line 924
  1. Read from $_POST, and $user_info is assigned
    in sources/controllers/Post.controller.php on line 924
  2. $msgOptions is assigned
    in sources/controllers/Post.controller.php on line 1032
  3. $msgOptions is passed to modifyPost()
    in sources/controllers/Post.controller.php on line 1039
  4. $messages_columns is assigned
    in sources/subs/Post.subs.php on line 446
  5. $messages_columns is passed through implode()
    in sources/subs/Post.subs.php on line 486
  6. ``' UPDATE {db_prefix}messages SET ' . implode(', ', $messages_columns) . ' WHERE id_msg = {int:id_msg}'`` is passed to Database_PostgreSQL::query()
    in sources/subs/Post.subs.php on line 484
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  2. Path: Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString() in sources/controllers/Search.controller.php on line 166
  1. Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString()
    in sources/controllers/Search.controller.php on line 166
  2. $string is passed through str_replace(), and str_replace(array('-', '_', '.'), array('+', '/', '='), $string) is decoded by base64_decode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 647
  3. !empty($temp_params2) ? $temp_params2 : $temp_params is passed through explode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 651
  4. $data is assigned
    in sources/subs/Search/Search.php on line 653
  5. $data is passed through explode(), and explode('|\'|', $data) is passed through array_pad(), and $v is assigned
    in sources/subs/Search/Search.php on line 655
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 656
  7. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 912
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1701
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  3. Path: Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString() in sources/controllers/Search.controller.php on line 302
  1. Read from $_REQUEST, and $_REQUEST['params'] is passed to Search::searchParamsFromString()
    in sources/controllers/Search.controller.php on line 302
  2. $string is passed through str_replace(), and str_replace(array('-', '_', '.'), array('+', '/', '='), $string) is decoded by base64_decode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 647
  3. !empty($temp_params2) ? $temp_params2 : $temp_params is passed through explode(), and $temp_params is assigned
    in sources/subs/Search/Search.php on line 651
  4. $data is assigned
    in sources/subs/Search/Search.php on line 653
  5. $data is passed through explode(), and explode('|\'|', $data) is passed through array_pad(), and $v is assigned
    in sources/subs/Search/Search.php on line 655
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 656
  7. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 912
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1701
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  4. Path: Read from $_REQUEST, and $_REQUEST is passed to Search::mergeSearchParams() in sources/controllers/Search.controller.php on line 304
  1. Read from $_REQUEST, and $_REQUEST is passed to Search::mergeSearchParams()
    in sources/controllers/Search.controller.php on line 304
  2. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 742
  3. Tainted property Search::$_search_params is read, and $this->_search_params['brd'] is passed through implode(), and Search::$_boardQuery is assigned
    in sources/subs/Search/Search.php on line 912
  4. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1678
  5. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}' . ($this->_createTemporary ? 'tmp_' : '') . 'log_search_topics (' . ($this->_createTemporary ? '' : 'id_search, ') . 'id_topic)' : '') . ' SELECT ' . ($this->_createTemporary ? '' : $id_search . ', ') . 't.id_topic FROM ' . $subject_query['from'] . (empty($subject_query['inner_join']) ? '' : ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($subject_query['inner_join']))) . (empty($subject_query['left_join']) ? '' : ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($subject_query['left_join']))) . ' WHERE ' . implode(' AND ', array_unique($subject_query['where'])) . (empty($modSettings['search_max_results']) ? '' : ' LIMIT ' . ($modSettings['search_max_results'] - $numSubjectResults))`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1701
  6. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  5. Path: Read from $_GET in sources/subs/Search/Search.php on line 966
  1. Read from $_GET
    in sources/subs/Search/Search.php on line 966
  2. Data is passed through htmlspecialchars_decode()
    in vendor/sources/Subs.php on line 569
  3. Data is passed through str_replace()
    in vendor/sources/Subs.php on line 570
  4. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 966
  5. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1176
  6. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1207
  7. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1215
  8. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1226
  9. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}log_search_results (' . implode(', ', array_keys($main_query['select'])) . ')' : '') . ' SELECT ' . implode(', ', $main_query['select']) . ' FROM ' . $main_query['from'] . (!empty($main_query['inner_join']) ? ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($main_query['inner_join'])) : '') . (!empty($main_query['left_join']) ? ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($main_query['left_join'])) : '') . (!empty($main_query['where']) ? ' WHERE ' : '') . implode(' AND ', array_unique($main_query['where'])) . (!empty($main_query['group_by']) ? ' GROUP BY ' . implode(', ', array_unique($main_query['group_by'])) : '') . (!empty($main_query['parameters']['limit']) ? ' LIMIT {int:limit}' : '')`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1925
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  6. Path: Read from $_POST, and Search::$_search_params is assigned in sources/subs/Search/Search.php on line 970
  1. Read from $_POST, and Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 970
  2. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1176
  3. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1207
  4. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1215
  5. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1226
  6. ``($this->_db->support_ignore() ? ' INSERT IGNORE INTO {db_prefix}log_search_results (' . implode(', ', array_keys($main_query['select'])) . ')' : '') . ' SELECT ' . implode(', ', $main_query['select']) . ' FROM ' . $main_query['from'] . (!empty($main_query['inner_join']) ? ' INNER JOIN ' . implode(' INNER JOIN ', array_unique($main_query['inner_join'])) : '') . (!empty($main_query['left_join']) ? ' LEFT JOIN ' . implode(' LEFT JOIN ', array_unique($main_query['left_join'])) : '') . (!empty($main_query['where']) ? ' WHERE ' : '') . implode(' AND ', array_unique($main_query['where'])) . (!empty($main_query['group_by']) ? ' GROUP BY ' . implode(', ', array_unique($main_query['group_by'])) : '') . (!empty($main_query['parameters']['limit']) ? ' LIMIT {int:limit}' : '')`` is passed to DbSearch_MySQL::search_query()
    in sources/subs/Search/Search.php on line 1925
  7. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 50
  8. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  7. Path: Read from $_POST, and $_POST is passed to Data_Validator::is_valid() in sources/controllers/Post.controller.php on line 883
  1. Read from $_POST, and $_POST is passed to Data_Validator::is_valid()
    in sources/controllers/Post.controller.php on line 883
  2. $data is passed to Data_Validator::validate()
    in sources/subs/DataValidator.class.php on line 147
  3. Data_Validator::$_data is assigned
    in sources/subs/DataValidator.class.php on line 249
  4. Tainted property Data_Validator::$_data is read
    in sources/subs/DataValidator.class.php on line 281
  5. Data_Validator::validation_data() returns tainted data
    in sources/subs/HttpReq.class.php on line 359
  6. HttpReq::cleanValue() returns tainted data, and HttpReq::$_param is assigned
    in sources/subs/HttpReq.class.php on line 219
  7. Tainted property HttpReq::$_param is read
    in sources/subs/HttpReq.class.php on line 278
  8. HttpReq::getQuery() returns tainted data, and $sort is assigned
    in sources/controllers/Memberlist.controller.php on line 407
  9. $field is assigned
    in sources/controllers/Memberlist.controller.php on line 501
  10. $customJoin is assigned
    in sources/controllers/Memberlist.controller.php on line 505
  11. $customJoin is passed through array_unique(), and array_unique($customJoin) is passed to ml_searchMembers()
    in sources/controllers/Memberlist.controller.php on line 518
  12. $customJoin is passed through implode()
    in sources/subs/Memberlist.subs.php on line 237
  13. ``' SELECT COUNT(*) FROM {db_prefix}members AS mem LEFT JOIN {db_prefix}membergroups AS mg ON (mg.id_group = CASE WHEN mem.id_group = {int:regular_id_group} THEN mem.id_post_group ELSE mem.id_group END) ' . (empty($customJoin) ? '' : implode(' ', $customJoin)) . ' WHERE (' . $where . ') AND mem.is_activated = {int:is_activated}'`` is passed to Database_PostgreSQL::query()
    in sources/subs/Memberlist.subs.php on line 232
  14. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  8. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned in sources/Request.php on line 207
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 207
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  9. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 209
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 209
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  10. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned in sources/Request.php on line 216
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and $_SERVER['HTTP_CLIENT_IP'] is passed through explode(), and explode('.', $_SERVER['HTTP_CLIENT_IP']) is passed through array_reverse(), and array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])) is passed through implode(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 216
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  11. Path: Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 218
  1. Fetching key HTTP_CLIENT_IP from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 218
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  12. Path: Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and $_SERVER['HTTP_X_FORWARDED_FOR'] is passed through explode(), and explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']) is passed through array_reverse(), and $ips is assigned in sources/Request.php on line 225
  1. Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and $_SERVER['HTTP_X_FORWARDED_FOR'] is passed through explode(), and explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']) is passed through array_reverse(), and $ips is assigned
    in sources/Request.php on line 225
  2. $ip is assigned
    in sources/Request.php on line 228
  3. $ip is passed through trim(), and Request::$_ban_ip is assigned
    in sources/Request.php on line 235
  4. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  5. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  6. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  8. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  9. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  10. $var is assigned
    in sources/subs/Members.subs.php on line 677
  11. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  12. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  13. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  14. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  15. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  16. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  17. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185
  13. Path: Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and Request::$_ban_ip is assigned in sources/Request.php on line 241
  1. Fetching key HTTP_X_FORWARDED_FOR from $_SERVER, and Request::$_ban_ip is assigned
    in sources/Request.php on line 241
  2. Tainted property Request::$_ban_ip is read
    in sources/Request.php on line 100
  3. Request::ban_ip() returns tainted data, and $regOptions is assigned
    in sources/controllers/Register.controller.php on line 519
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 520
  5. $regOptions is assigned
    in sources/subs/Members.subs.php on line 502
  6. $regOptions is assigned
    in sources/subs/Members.subs.php on line 586
  7. $regOptions is assigned
    in sources/subs/Members.subs.php on line 628
  8. $var is assigned
    in sources/subs/Members.subs.php on line 677
  9. $column_names is assigned
    in sources/subs/Members.subs.php on line 687
  10. $column_names is passed to Database_PostgreSQL::insert()
    in sources/subs/Members.subs.php on line 694
  11. $columnName is assigned
    in sources/database/Db-postgresql.class.php on line 584
  12. $columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ' is passed through sprintf(), and $actualType is assigned
    in sources/database/Db-postgresql.class.php on line 588
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 594
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 603
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 185

Preventing SQL Injection

There are two options to prevent SQL injection. Generally, it is recommended to use parameter binding:

$stmt = mysqli_prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $taintedUserName);

An alternative – although generally not recommended – is to escape your data manually:

$mysqli = new mysqli('localhost', 'user', 'pass', 'dbname');

$escaped = $mysqli->real_escape_string($taintedUserName);
$mysqli->query("SELECT * FROM users WHERE name = '".$escaped."'");

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
304
305
		// @deprecated since 1.1 - use skip_next_error method
306
		if (!empty($db_values['db_error_skip']))
307
		{
308
			$this->_skip_error = true;
309
		}
310
311
		if ($this->_db_last_result === false && !$this->_skip_error)
312
		{
313
			$this->error($db_string, $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::error() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
314
		}
315
316
		// Revert not to skip errors
317
		if ($this->_skip_error === true)
318
		{
319
			$this->_skip_error = false;
320
		}
321
322
		if ($this->_in_transaction)
323
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 128 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
324
325
		// Debugging.
326 View Code Duplication
		if ($db_show_debug === true)
327
		{
328
			$db_cache['t'] = microtime(true) - $st;
0 ignored issues
show
Bug introduced by
The variable $db_cache does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $st does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
329
			$debug->db_query($db_cache);
0 ignored issues
show
Bug introduced by
The variable $debug does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
330
		}
331
332
		return $this->_db_last_result;
333
	}
334
335
	/**
336
	 * Affected rows from previous operation.
337
	 *
338
	 * @param resource|null $result
339
	 */
340
	public function affected_rows($result = null)
341
	{
342
		if ($this->_db_replace_result !== null)
343
			return $this->_db_replace_result;
344
		elseif ($result === null && !$this->_db_last_result)
345
			return 0;
346
347
		return pg_affected_rows($result === null ? $this->_db_last_result : $result);
348
	}
349
350
	/**
351
	 * Last inserted id.
352
	 *
353
	 * @param string $table
354
	 * @param string|null $field = null
355
	 * @param resource|null $connection = null
356
	 * @throws Elk_Exception
357
	 */
358
	public function insert_id($table, $field = null, $connection = null)
359
	{
360
		global $db_prefix;
361
362
		$table = str_replace('{db_prefix}', $db_prefix, $table);
363
364
		$connection = $connection === null ? $this->_connection : $connection;
365
366
		// Try get the last ID for the auto increment field.
367
		$request = $this->query('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
368
			array(
369
			),
370
			$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 364 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
371
		);
372
373
		if (!$request)
374
			return false;
375
376
		list ($lastID) = $this->fetch_row($request);
0 ignored issues
show
Documentation introduced by
$request is of type boolean|string, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
377
		$this->free_result($request);
0 ignored issues
show
Documentation introduced by
$request is of type boolean|string, but the function expects a resource.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
378
379
		return $lastID;
380
	}
381
382
	/**
383
	 * Tracking the current row.
384
	 * Fetch a row from the resultset given as parameter.
385
	 *
386
	 * @param resource $request
387
	 * @param integer|bool $counter = false
388
	 */
389 View Code Duplication
	public function fetch_row($request, $counter = false)
390
	{
391
		global $db_row_count;
392
393
		if ($counter !== false)
394
			return pg_fetch_row($request, $counter);
395
396
		// Reset the row counter...
397
		if (!isset($db_row_count[(int) $request]))
398
			$db_row_count[(int) $request] = 0;
399
400
		// Return the right row.
401
		return @pg_fetch_row($request, $db_row_count[(int) $request]++);
402
	}
403
404
	/**
405
	 * Free the resultset.
406
	 *
407
	 * @param resource $result
408
	 */
409
	public function free_result($result)
410
	{
411
		// Just delegate to the native function
412
		pg_free_result($result);
413
	}
414
415
	/**
416
	 * Get the number of rows in the result.
417
	 *
418
	 * @param resource $result
419
	 */
420
	public function num_rows($result)
421
	{
422
		// simply delegate to the native function
423
		return pg_num_rows($result);
424
	}
425
426
	/**
427
	 * Get the number of fields in the resultset.
428
	 *
429
	 * @param resource $request
430
	 */
431
	public function num_fields($request)
432
	{
433
		return pg_num_fields($request);
434
	}
435
436
	/**
437
	 * Reset the internal result pointer.
438
	 *
439
	 * @param boolean $request
440
	 * @param integer $counter
441
	 */
442
	public function data_seek($request, $counter)
443
	{
444
		global $db_row_count;
445
446
		$db_row_count[(int) $request] = $counter;
447
448
		return true;
449
	}
450
451
	/**
452
	 * Do a transaction.
453
	 *
454
	 * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
455
	 * @param resource|null $connection = null
456
	 */
457
	public function db_transaction($type = 'commit', $connection = null)
458
	{
459
		// Decide which connection to use
460
		$connection = $connection === null ? $this->_connection : $connection;
461
462
		if ($type == 'begin')
463
		{
464
			$this->_in_transaction = true;
465
			return @pg_query($connection, 'BEGIN');
466
		}
467
		elseif ($type == 'rollback')
468
			return @pg_query($connection, 'ROLLBACK');
469
		elseif ($type == 'commit')
470
		{
471
			$this->_in_transaction = false;
472
			return @pg_query($connection, 'COMMIT');
473
		}
474
475
		return false;
476
	}
477
478
	/**
479
	 * Return last error string from the database server
480
	 *
481
	 * @param resource|null $connection = null
482
	 */
483
	public function last_error($connection = null)
484
	{
485
		// Decide which connection to use
486
		$connection = $connection === null ? $this->_connection : $connection;
487
488
		if (is_resource($connection))
489
			return pg_last_error($connection);
490
	}
491
492
	/**
493
	 * Database error.
494
	 * Backtrace, log, try to fix.
495
	 *
496
	 * @param string        $db_string
497
	 * @param resource|null $connection = null
498
	 *
499
	 * @throws Elk_Exception
500
	 */
501
	public function error($db_string, $connection = null)
502
	{
503
		global $txt, $context, $modSettings, $db_show_debug;
504
505
		// We'll try recovering the file and line number the original db query was called from.
506
		list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
507
508
		// Decide which connection to use
509
		$connection = $connection === null ? $this->_connection : $connection;
510
511
		// This is the error message...
512
		$query_error = @pg_last_error($connection);
513
514
		// Log the error.
515 View Code Duplication
		if (class_exists('Errors'))
516
		{
517
			Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line);
518
		}
519
520
		// Nothing's defined yet... just die with it.
521
		if (empty($context) || empty($txt))
522
			die($query_error);
0 ignored issues
show
Coding Style Compatibility introduced by
The method error() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
523
524
		// Show an error message, if possible.
525
		$context['error_title'] = $txt['database_error'];
526 View Code Duplication
		if (allowedTo('admin_forum'))
527
			$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line;
528
		else
529
			$context['error_message'] = $txt['try_again'];
530
531
		// Add database version that we know of, for the admin to know. (and ask for support)
532 View Code Duplication
		if (allowedTo('admin_forum'))
533
			$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $modSettings['elkVersion']);
534
535 View Code Duplication
		if (allowedTo('admin_forum') && $db_show_debug === true)
536
			$context['error_message'] .= '<br /><br />' . nl2br($db_string);
537
538
		// It's already been logged... don't log it again.
539
		throw new Elk_Exception($context['error_message'], false);
540
	}
541
542
	/**
543
	 * Insert data.
544
	 *
545
	 * @param string $method - options 'replace', 'ignore', 'insert'
546
	 * @param string $table
547
	 * @param mixed[] $columns
548
	 * @param mixed[] $data
549
	 * @param mixed[] $keys
550
	 * @param bool $disable_trans = false
551
	 * @param resource|null $connection = null
552
	 * @throws Elk_Exception
553
	 */
554
	public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
555
	{
556
		global $db_prefix;
557
558
		$connection = $connection === null ? $this->_connection : $connection;
559
560
		// With nothing to insert, simply return.
561
		if (empty($data))
562
			return;
563
564
		// Inserting data as a single row can be done as a single array.
565
		if (!is_array($data[array_rand($data)]))
566
			$data = array($data);
567
568
		// Replace the prefix holder with the actual prefix.
569
		$table = str_replace('{db_prefix}', $db_prefix, $table);
570
571
		$priv_trans = false;
572
		if ((count($data) > 1 || $method == 'replace') && !$this->_in_transaction && !$disable_trans)
573
		{
574
			$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
575
			$priv_trans = true;
576
		}
577
578
		// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
579
		if ($method == 'replace')
580
		{
581
			$count = 0;
582
			$where = '';
583
			$db_replace_result = 0;
584
			foreach ($columns as $columnName => $type)
585
			{
586
				// Are we restricting the length?
587
				if (strpos($type, 'string-') !== false)
588
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
589
				else
590
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
591
592
				// A key? That's what we were looking for.
593
				if (in_array($columnName, $keys))
594
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
595
				$count++;
596
			}
597
598
			// Make it so.
599
			if (!empty($where) && !empty($data))
600
			{
601
				foreach ($data as $k => $entry)
602
				{
603
					$this->query('', '
604
						DELETE FROM ' . $table .
605
						' WHERE ' . $where,
606
						$entry, $connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
607
					);
608
					$db_replace_result += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
609
				}
610
			}
611
		}
612
613
		if (!empty($data))
614
		{
615
			// Create the mold for a single row insert.
616
			$insertData = '(';
617 View Code Duplication
			foreach ($columns as $columnName => $type)
618
			{
619
				// Are we restricting the length?
620
				if (strpos($type, 'string-') !== false)
621
					$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
622
				else
623
					$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
624
			}
625
			$insertData = substr($insertData, 0, -2) . ')';
626
627
			// Create an array consisting of only the columns.
628
			$indexed_columns = array_keys($columns);
629
630
			// Here's where the variables are injected to the query.
631
			$insertRows = array();
632
			foreach ($data as $dataRow)
633
				$insertRows[] = $this->quote($insertData, $this->_array_combine($indexed_columns, $dataRow), $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 can also be of type resource; however, Database_Abstract::quote() does only seem to accept object<mysqli>|object<postgre>|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
634
635
			$inserted_results = 0;
636
			$skip_error = $method == 'ignore' || $table === $db_prefix . 'log_errors';
637
			foreach ($insertRows as $entry)
638
			{
639
				$this->_skip_error = $skip_error;
640
641
				// Do the insert.
642
				$this->query('', '
643
					INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
644
					VALUES
645
						' . $entry,
646
					array(
647
						'security_override' => true,
648
					),
649
					$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 can also be of type object<mysqli>; however, Database_PostgreSQL::query() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
650
				);
651
				$inserted_results += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
652
			}
653
			if (isset($db_replace_result))
654
				$this->_db_replace_result = $db_replace_result + $inserted_results;
655
		}
656
657
		if ($priv_trans)
658
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 558 can also be of type object<mysqli>; however, Database_PostgreSQL::db_transaction() does only seem to accept resource|null, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
659
	}
660
661
	/**
662
	 * Unescape an escaped string!
663
	 *
664
	 * @param string $string
665
	 */
666
	public function unescape_string($string)
667
	{
668
		return strtr($string, array('\'\'' => '\''));
669
	}
670
671
	/**
672
	 * Returns whether the database system supports ignore.
673
	 *
674
	 * @return false
675
	 */
676
	public function support_ignore()
677
	{
678
		return false;
679
	}
680
681
	/**
682
	 * Gets all the necessary INSERTs for the table named table_name.
683
	 * It goes in 250 row segments.
684
	 *
685
	 * @param string $tableName - the table to create the inserts for.
686
	 * @param bool $new_table
687
	 *
688
	 * @return string the query to insert the data back in, or an empty string if the table was empty.
689
	 * @throws Elk_Exception
690
	 */
691
	public function insert_sql($tableName, $new_table = false)
692
	{
693
		global $db_prefix;
694
695
		static $start = 0, $num_rows, $fields, $limit;
696
697 View Code Duplication
		if ($new_table)
698
		{
699
			$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
700
			$start = 0;
701
		}
702
703
		$data = '';
704
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
705
706
		// This will be handy...
707
		$crlf = "\r\n";
708
709
		$result = $this->query('', '
710
			SELECT *
711
			FROM ' . $tableName . '
712
			LIMIT ' . $start . ', ' . $limit,
713
			array(
714
				'security_override' => true,
715
			)
716
		);
717
718
		// The number of rows, just for record keeping and breaking INSERTs up.
719
		$num_rows = $this->num_rows($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 709 can also be of type boolean or string; however, Database_PostgreSQL::num_rows() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
720
721
		if ($num_rows == 0)
722
			return '';
723
724
		if ($new_table)
725
		{
726
			$fields = array_keys($this->fetch_assoc($result));
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 709 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
727
			$this->data_seek($result, 0);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 709 can also be of type resource or string; however, Database_PostgreSQL::data_seek() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
728
		}
729
730
		// Start it off with the basic INSERT INTO.
731
		$insert_msg = 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
732
733
		// Loop through each row.
734 View Code Duplication
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 709 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
735
		{
736
			// Get the fields in this row...
737
			$field_list = array();
738
739
			foreach ($row as $key => $item)
740
			{
741
				// Try to figure out the type of each field. (NULL, number, or 'string'.)
742
				if (!isset($item))
743
					$field_list[] = 'NULL';
744
				elseif (is_numeric($item) && (int) $item == $item)
745
					$field_list[] = $item;
746
				else
747
					$field_list[] = '\'' . $this->escape_string($item) . '\'';
748
			}
749
750
			// 'Insert' the data.
751
			$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
752
		}
753
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...ity_override' => true)) on line 709 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
754
755
		$data .= $crlf;
756
757
		$start += $limit;
758
759
		return $data;
760
	}
761
762
	/**
763
	 * Dumps the schema (CREATE) for a table.
764
	 *
765
	 * @param string $tableName - the table
766
	 *
767
	 * @return string - the CREATE statement as string
768
	 * @throws Elk_Exception
769
	 */
770
	public function db_table_sql($tableName)
771
	{
772
		global $db_prefix;
773
774
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
775
776
		// This will be needed...
777
		$crlf = "\r\n";
778
779
		// Start the create table...
780
		$schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf;
781
		$index_create = '';
782
		$seq_create = '';
783
784
		// Find all the fields.
785
		$result = $this->query('', '
786
			SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
787
			FROM information_schema.columns
788
			WHERE table_name = {string:table}
789
			ORDER BY ordinal_position',
790
			array(
791
				'table' => $tableName,
792
			)
793
		);
794
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 785 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
795
		{
796
			if ($row['data_type'] == 'character varying')
797
				$row['data_type'] = 'varchar';
798
			elseif ($row['data_type'] == 'character')
799
				$row['data_type'] = 'char';
800
801
			if ($row['character_maximum_length'])
802
				$row['data_type'] .= '(' . $row['character_maximum_length'] . ')';
803
804
			// Make the CREATE for this column.
805
			$schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : '');
806
807
			// Add a default...?
808
			if (trim($row['column_default']) != '')
809
			{
810
				$schema_create .= ' default ' . $row['column_default'] . '';
811
812
				// Auto increment?
813
				if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
814
				{
815
					// Get to find the next variable first!
816
					$count_req = $this->query('', '
817
						SELECT MAX("{raw:column}")
818
						FROM {raw:table}',
819
						array(
820
							'column' => $row['column_name'],
821
							'table' => $tableName,
822
						)
823
					);
824
					list ($max_ind) = $this->fetch_row($count_req);
0 ignored issues
show
Bug introduced by
It seems like $count_req can also be of type boolean or string; however, Database_PostgreSQL::fetch_row() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
825
					$this->free_result($count_req);
0 ignored issues
show
Bug introduced by
It seems like $count_req defined by $this->query('', ' ...'table' => $tableName)) on line 816 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
826
827
					// Get the right bloody start!
828
					$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
829
				}
830
			}
831
832
			$schema_create .= ',' . $crlf;
833
		}
834
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 785 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
835
836
		// Take off the last comma.
837
		$schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
838
839
		$result = $this->query('', '
840
			SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef
841
			FROM pg_class AS c
842
				INNER JOIN pg_index AS i ON (i.indrelid = c.oid)
843
				INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid)
844
			WHERE c.relname = {string:table}',
845
			array(
846
				'table' => $tableName,
847
			)
848
		);
849
850
		while ($row = $this->fetch_assoc($result))
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 839 can also be of type boolean or string; however, Database_PostgreSQL::fetch_assoc() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
851
		{
852
			if ($row['is_primary'])
853
			{
854
				if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
855
					continue;
856
857
				$index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");';
858
			}
859
			else
860
				$index_create .= $crlf . $row['inddef'] . ';';
861
		}
862
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 839 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
863
864
		// Finish it off!
865
		$schema_create .= $crlf . ');';
866
867
		return $seq_create . $schema_create . $index_create;
868
	}
869
870
	/**
871
	 * {@inheritdoc}
872
	 */
873
	public function db_list_tables($db_name_str = false, $filter = false)
874
	{
875
		$request = $this->query('', '
876
			SELECT tablename
877
			FROM pg_tables
878
			WHERE schemaname = {string:schema_public}' . ($filter === false ? '' : '
879
				AND tablename LIKE {string:filter}') . '
880
			ORDER BY tablename',
881
			array(
882
				'schema_public' => 'public',
883
				'filter' => $filter,
884
			)
885
		);
886
		$tables = array();
887
		while ($row = $this->fetch_row($request))
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 875 can also be of type boolean or string; however, Database_PostgreSQL::fetch_row() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
888
			$tables[] = $row[0];
889
		$this->free_result($request);
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 875 can also be of type boolean or string; however, Database_PostgreSQL::free_result() does only seem to accept resource, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
890
891
		return $tables;
892
	}
893
894
	/**
895
	 * Backup $table to $backup_table.
896
	 *
897
	 * @param string $table
898
	 * @param string $backup_table
899
	 * @throws Elk_Exception
900
	 */
901
	public function db_backup_table($table, $backup_table)
902
	{
903
		global $db_prefix;
904
905
		$table = str_replace('{db_prefix}', $db_prefix, $table);
906
907
		// Do we need to drop it first?
908
		$db_table = db_table();
909
		$db_table->db_drop_table($backup_table);
910
911
		// @todo Should we create backups of sequences as well?
912
		$this->query('', '
913
			CREATE TABLE {raw:backup_table}
914
			(
915
				LIKE {raw:table}
916
				INCLUDING DEFAULTS
917
			)',
918
			array(
919
				'backup_table' => $backup_table,
920
				'table' => $table,
921
			)
922
		);
923
924
		$this->query('', '
925
			INSERT INTO {raw:backup_table}
926
			SELECT * FROM {raw:table}',
927
			array(
928
				'backup_table' => $backup_table,
929
				'table' => $table,
930
			)
931
		);
932
	}
933
934
	/**
935
	 * Get the server version number.
936
	 *
937
	 * @return string - the version
938
	 */
939
	public function db_server_version()
940
	{
941
		$version = pg_version();
942
943
		return $version['server'];
944
	}
945
946
	/**
947
	 * Get the name (title) of the database system.
948
	 *
949
	 * @return string
950
	 */
951
	public function db_title()
952
	{
953
		return 'PostgreSQL';
954
	}
955
956
	/**
957
	 * Whether the database system is case sensitive.
958
	 *
959
	 * @return boolean
960
	 */
961
	public function db_case_sensitive()
962
	{
963
		return true;
964
	}
965
966
	/**
967
	 * Quotes identifiers for replacement__callback.
968
	 *
969
	 * @param mixed $replacement
970
	 * @return string
971
	 * @throws Elk_Exception
972
	 */
973 View Code Duplication
	protected function _replaceIdentifier($replacement)
974
	{
975
		if (preg_match('~[a-z_][0-9,a-z,A-Z$_]{0,60}~', $replacement) !== 1)
976
		{
977
			$this->error_backtrace('Wrong value type sent to the database. Invalid identifier used. (' . $replacement . ')', '', E_USER_ERROR, __FILE__, __LINE__);
978
		}
979
980
		return '"' . $replacement . '"';
981
	}
982
983
	/**
984
	 * Escape string for the database input
985
	 *
986
	 * @param string $string
987
	 */
988
	public function escape_string($string)
989
	{
990
		return pg_escape_string($string);
991
	}
992
993
	/**
994
	 * Fetch next result as association.
995
	 *
996
	 * @param resource $request
997
	 * @param int|bool $counter = false
998
	 */
999 View Code Duplication
	public function fetch_assoc($request, $counter = false)
1000
	{
1001
		global $db_row_count;
1002
1003
		if ($counter !== false)
1004
			return pg_fetch_assoc($request, $counter);
1005
1006
		// Reset the row counter...
1007
		if (!isset($db_row_count[(int) $request]))
1008
			$db_row_count[(int) $request] = 0;
1009
1010
		// Return the right row.
1011
		return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
1012
	}
1013
1014
	/**
1015
	 * Return server info.
1016
	 *
1017
	 * @return string
1018
	 */
1019
	public function db_server_info()
1020
	{
1021
		// give info on client! we use it in install and upgrade and such things.
1022
		$version = pg_version();
1023
1024
		return $version['client'];
1025
	}
1026
1027
	/**
1028
	 * Return client version.
1029
	 *
1030
	 * @return string - the version
1031
	 */
1032
	public function db_client_version()
1033
	{
1034
		$version = pg_version();
1035
1036
		return $version['client'];
1037
	}
1038
1039
	/**
1040
	 * Dummy function really. Doesn't do anything on PostgreSQL.
1041
	 *
1042
	 * @param string|null $db_name = null
1043
	 * @param resource|null $connection = null
1044
	 *
1045
	 * @return boolean
1046
	 */
1047
	public function select_db($db_name = null, $connection = null)
1048
	{
1049
		return true;
1050
	}
1051
1052
	/**
1053
	 * Returns a reference to the existing instance
1054
	 */
1055
	public static function db()
1056
	{
1057
		return self::$_db;
1058
	}
1059
1060
	/**
1061
	 * Finds out if the connection is still valid.
1062
	 *
1063
	 * @param postgre|null $connection = null
1064
	 */
1065
	public function validConnection($connection = null)
1066
	{
1067
		return is_resource($connection);
1068
	}
1069
}
1070