Completed
Pull Request — development (#3089)
by John
09:06
created

Database_PostgreSQL::insert_id()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 23
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
nc 4
nop 3
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
ccs 0
cts 16
cp 0
crap 12
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 2.0 dev
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
		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
			'attach_download_increase' => array(
147
				'~LOW_PRIORITY~' => '',
148
			),
149
			'insert_log_search_topics' => array(
150
				'~NOT RLIKE~' => '!~',
151
			),
152
			'insert_log_search_results_no_index' => array(
153
				'~NOT RLIKE~' => '!~',
154
			),
155
			'insert_log_search_results_subject' => array(
156
				'~NOT RLIKE~' => '!~',
157
			),
158
			'pm_conversation_list' => array(
159
				'~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']) : ''),
160
			),
161
			'profile_board_stats' => array(
162
				'~COUNT\(\*\) \/ MAX\(b.num_posts\)~' => 'CAST(COUNT(*) AS DECIMAL) / CAST(b.num_posts AS DECIMAL)',
163
			),
164
			'case_insensitive_like' => array(
165
				'~LIKE~' => 'ILIKE',
166
			),
167
		);
168
169
		if (isset($replacements[$identifier]))
170
			$db_string = preg_replace(array_keys($replacements[$identifier]), array_values($replacements[$identifier]), $db_string);
171
172
		// Limits need to be a little different.
173
		$db_string = preg_replace('~\sLIMIT\s(\d+|{int:.+}),\s*(\d+|{int:.+})\s*$~i', 'LIMIT $2 OFFSET $1', $db_string);
174
175
		if (trim($db_string) == '')
176
			return false;
177
178
		// Comments that are allowed in a query are preg_removed.
179
		static $allowed_comments_from = array(
180
			'~\s+~s',
181
			'~/\*!40001 SQL_NO_CACHE \*/~',
182
			'~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~',
183
			'~/\*!40100 ON DUPLICATE KEY UPDATE id_msg = \d+ \*/~',
184
		);
185
		static $allowed_comments_to = array(
186
			' ',
187
			'',
188
			'',
189
			'',
190
		);
191
192
		// One more query....
193
		$this->_query_count++;
194
		$this->_db_replace_result = null;
195
196 View Code Duplication
		if (empty($modSettings['disableQueryCheck']) && strpos($db_string, '\'') !== false && empty($db_values['security_override']))
197
			$this->error_backtrace('Hacking attempt...', 'Illegal character (\') used in query...', true, __FILE__, __LINE__);
198
199 View Code Duplication
		if (empty($db_values['security_override']) && (!empty($db_values) || strpos($db_string, '{db_prefix}') !== false))
200
		{
201
			// Store these values for use in the callback function.
202
			$this->_db_callback_values = $db_values;
203
			$this->_db_callback_connection = $connection;
204
205
			// Inject the values passed to this function.
206
			$db_string = preg_replace_callback('~{([a-z_]+)(?::([a-zA-Z0-9_-]+))?}~', array($this, 'replacement__callback'), $db_string);
207
208
			// No need for them any longer.
209
			$this->_db_callback_values = array();
210
			$this->_db_callback_connection = null;
211
		}
212
213
		// Debugging.
214 View Code Duplication
		if ($db_show_debug === true)
215
		{
216
			$debug = Debug::instance();
217
218
			// Get the file and line number this function was called.
219
			list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
220
221
			if (!empty($_SESSION['debug_redirect']))
222
			{
223
				$debug->merge_db($_SESSION['debug_redirect']);
224
				// @todo this may be off by 1
225
				$this->_query_count += count($_SESSION['debug_redirect']);
226
				$_SESSION['debug_redirect'] = array();
227
			}
228
229
			// Don't overload it.
230
			$st = microtime(true);
231
			$db_cache = array();
232
			$db_cache['q'] = $this->_query_count < 50 ? $db_string : '...';
233
			$db_cache['f'] = $file;
234
			$db_cache['l'] = $line;
235
			$db_cache['s'] = $st - $time_start;
236
		}
237
238
		// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
239
		if (empty($modSettings['disableQueryCheck']))
240
		{
241
			$clean = '';
242
			$old_pos = 0;
243
			$pos = -1;
244 View Code Duplication
			while (true)
245
			{
246
				$pos = strpos($db_string, '\'', $pos + 1);
247
				if ($pos === false)
248
					break;
249
				$clean .= substr($db_string, $old_pos, $pos - $old_pos);
250
251
				while (true)
252
				{
253
					$pos1 = strpos($db_string, '\'', $pos + 1);
254
					$pos2 = strpos($db_string, '\'\'', $pos + 1);
255
256
					if ($pos1 === false)
257
						break;
258
					elseif ($pos2 === false || $pos2 > $pos1)
259
					{
260
						$pos = $pos1;
261
						break;
262
					}
263
264
					$pos = $pos2 + 1;
265
				}
266
267
				$clean .= ' %s ';
268
				$old_pos = $pos + 1;
269
			}
270
271
			$clean .= substr($db_string, $old_pos);
272
			$clean = trim(strtolower(preg_replace($allowed_comments_from, $allowed_comments_to, $clean)));
273
274
			// Comments?  We don't use comments in our queries, we leave 'em outside!
275 View Code Duplication
			if (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false || strpos($clean, ';') !== false)
276
				$fail = true;
277
			// Trying to change passwords, slow us down, or something?
278
			elseif (strpos($clean, 'sleep') !== false && preg_match('~(^|[^a-z])sleep($|[^[_a-z])~s', $clean) != 0)
279
				$fail = true;
280
			elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
281
				$fail = true;
282
283
			if (!empty($fail) && class_exists('Errors'))
284
				$this->error_backtrace('Hacking attempt...', 'Hacking attempt...' . "\n" . $db_string, E_USER_ERROR, __FILE__, __LINE__);
285
286
			// If we are updating something, better start a transaction so that indexes may be kept consistent
287
			if (!$this->_in_transaction && strpos($clean, 'update') !== false)
288
				$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...
289
		}
290
291
		$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 173
  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 643
  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 647
  4. $data is assigned
    in sources/subs/Search/Search.php on line 649
  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 651
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 652
  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 908
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  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 1695
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 643
  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 647
  4. $data is assigned
    in sources/subs/Search/Search.php on line 649
  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 651
  6. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 652
  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 908
  8. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  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 1695
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 738
  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 908
  4. Tainted property Search::$_boardQuery is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1672
  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 1695
  6. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  7. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  5. Path: Read from $_GET in sources/subs/Search/Search.php on line 962
  1. Read from $_GET
    in sources/subs/Search/Search.php on line 962
  2. Data is passed through htmlspecialchars_decode()
    in vendor/sources/Subs.php on line 543
  3. Data is passed through str_replace()
    in vendor/sources/Subs.php on line 544
  4. Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 962
  5. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1172
  6. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1203
  7. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1211
  8. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1222
  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 1919
  10. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  11. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  6. Path: Read from $_POST, and Search::$_search_params is assigned in sources/subs/Search/Search.php on line 966
  1. Read from $_POST, and Search::$_search_params is assigned
    in sources/subs/Search/Search.php on line 966
  2. Tainted property Search::$_search_params is read, and $subject_query is assigned
    in sources/subs/Search/Search.php on line 1172
  3. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1203
  4. $subject_query is assigned
    in sources/subs/Search/Search.php on line 1211
  5. $subject_query is passed to Search::_build_search_results_log()
    in sources/subs/Search/Search.php on line 1222
  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 1919
  7. $db_string is passed to Database_PostgreSQL::query()
    in sources/database/DbSearch-mysql.php on line 62
  8. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 235
  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 230
  14. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  6. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  15. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  16. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  17. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173
  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 487
  4. $regOptions is passed to registerMember()
    in sources/controllers/Register.controller.php on line 488
  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 566
  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 570
  13. $actualType is passed through substr(), and $where is assigned
    in sources/database/Db-postgresql.class.php on line 576
  14. ``' DELETE FROM ' . $table . ' WHERE ' . $where`` is passed to Database_PostgreSQL::query()
    in sources/database/Db-postgresql.class.php on line 585
  15. $db_string is passed through preg_replace(), and $db_string is assigned
    in sources/database/Db-postgresql.class.php on line 173

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...
292
293
		if ($this->_db_last_result === false && !$this->_skip_error)
294
		{
295
			$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...
296
		}
297
298
		// Revert not to skip errors
299
		if ($this->_skip_error === true)
300
		{
301
			$this->_skip_error = false;
302
		}
303
304
		if ($this->_in_transaction)
305
			$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...
306
307
		// Debugging.
308 View Code Duplication
		if ($db_show_debug === true)
309
		{
310
			$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...
311
			$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...
312
		}
313
314
		return $this->_db_last_result;
315
	}
316
317
	/**
318
	 * Affected rows from previous operation.
319
	 *
320
	 * @param resource|null $result
321
	 */
322
	public function affected_rows($result = null)
323
	{
324
		if ($this->_db_replace_result !== null)
325
			return $this->_db_replace_result;
326
		elseif ($result === null && !$this->_db_last_result)
327
			return 0;
328
329
		return pg_affected_rows($result === null ? $this->_db_last_result : $result);
330
	}
331
332
	/**
333
	 * Last inserted id.
334
	 *
335
	 * @param string $table
336
	 * @param string|null $field = null
337
	 * @param resource|null $connection = null
338
	 * @throws Elk_Exception
339
	 */
340
	public function insert_id($table, $field = null, $connection = null)
341
	{
342
		global $db_prefix;
343
344
		$table = str_replace('{db_prefix}', $db_prefix, $table);
345
346
		$connection = $connection === null ? $this->_connection : $connection;
347
348
		// Try get the last ID for the auto increment field.
349
		$request = $this->query('', 'SELECT CURRVAL(\'' . $table . '_seq\') AS insertID',
350
			array(
351
			),
352
			$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 346 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...
353
		);
354
355
		if (!$request)
356
			return false;
357
358
		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...
359
		$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...
360
361
		return $lastID;
362
	}
363
364
	/**
365
	 * Tracking the current row.
366
	 * Fetch a row from the resultset given as parameter.
367
	 *
368
	 * @param resource $request
369
	 * @param integer|bool $counter = false
370
	 */
371 View Code Duplication
	public function fetch_row($request, $counter = false)
372
	{
373
		global $db_row_count;
374
375
		if ($counter !== false)
376
			return pg_fetch_row($request, $counter);
377
378
		// Reset the row counter...
379
		if (!isset($db_row_count[(int) $request]))
380
			$db_row_count[(int) $request] = 0;
381
382
		// Return the right row.
383
		return @pg_fetch_row($request, $db_row_count[(int) $request]++);
384
	}
385
386
	/**
387
	 * Free the resultset.
388
	 *
389
	 * @param resource $result
390
	 */
391
	public function free_result($result)
392
	{
393
		// Just delegate to the native function
394
		pg_free_result($result);
395
	}
396
397
	/**
398
	 * Get the number of rows in the result.
399
	 *
400
	 * @param resource $result
401
	 */
402
	public function num_rows($result)
403
	{
404
		// simply delegate to the native function
405
		return pg_num_rows($result);
406
	}
407
408
	/**
409
	 * Get the number of fields in the resultset.
410
	 *
411
	 * @param resource $request
412
	 */
413
	public function num_fields($request)
414
	{
415
		return pg_num_fields($request);
416
	}
417
418
	/**
419
	 * Reset the internal result pointer.
420
	 *
421
	 * @param boolean $request
422
	 * @param integer $counter
423
	 */
424
	public function data_seek($request, $counter)
425
	{
426
		global $db_row_count;
427
428
		$db_row_count[(int) $request] = $counter;
429
430
		return true;
431
	}
432
433
	/**
434
	 * Do a transaction.
435
	 *
436
	 * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
437
	 * @param resource|null $connection = null
438
	 */
439
	public function db_transaction($type = 'commit', $connection = null)
440
	{
441
		// Decide which connection to use
442
		$connection = $connection === null ? $this->_connection : $connection;
443
444
		if ($type == 'begin')
445
		{
446
			$this->_in_transaction = true;
447
			return @pg_query($connection, 'BEGIN');
448
		}
449
		elseif ($type == 'rollback')
450
			return @pg_query($connection, 'ROLLBACK');
451
		elseif ($type == 'commit')
452
		{
453
			$this->_in_transaction = false;
454
			return @pg_query($connection, 'COMMIT');
455
		}
456
457
		return false;
458
	}
459
460
	/**
461
	 * Return last error string from the database server
462
	 *
463
	 * @param resource|null $connection = null
464
	 */
465
	public function last_error($connection = null)
466
	{
467
		// Decide which connection to use
468
		$connection = $connection === null ? $this->_connection : $connection;
469
470
		if (is_resource($connection))
471
			return pg_last_error($connection);
472
	}
473
474
	/**
475
	 * Database error.
476
	 * Backtrace, log, try to fix.
477
	 *
478
	 * @param string        $db_string
479
	 * @param resource|null $connection = null
480
	 *
481
	 * @throws Elk_Exception
482
	 */
483
	public function error($db_string, $connection = null)
484
	{
485
		global $txt, $context, $modSettings, $db_show_debug;
486
487
		// We'll try recovering the file and line number the original db query was called from.
488
		list ($file, $line) = $this->error_backtrace('', '', 'return', __FILE__, __LINE__);
489
490
		// Decide which connection to use
491
		$connection = $connection === null ? $this->_connection : $connection;
492
493
		// This is the error message...
494
		$query_error = @pg_last_error($connection);
495
496
		// Log the error.
497 View Code Duplication
		if (class_exists('Errors'))
498
		{
499
			Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n" . $db_string : ''), 'database', $file, $line);
500
		}
501
502
		// Nothing's defined yet... just die with it.
503
		if (empty($context) || empty($txt))
504
			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...
505
506
		// Show an error message, if possible.
507
		$context['error_title'] = $txt['database_error'];
508 View Code Duplication
		if (allowedTo('admin_forum'))
509
			$context['error_message'] = nl2br($query_error) . '<br />' . $txt['file'] . ': ' . $file . '<br />' . $txt['line'] . ': ' . $line;
510
		else
511
			$context['error_message'] = $txt['try_again'];
512
513
		// Add database version that we know of, for the admin to know. (and ask for support)
514 View Code Duplication
		if (allowedTo('admin_forum'))
515
			$context['error_message'] .= '<br /><br />' . sprintf($txt['database_error_versions'], $modSettings['elkVersion']);
516
517 View Code Duplication
		if (allowedTo('admin_forum') && $db_show_debug === true)
518
			$context['error_message'] .= '<br /><br />' . nl2br($db_string);
519
520
		// It's already been logged... don't log it again.
521
		throw new Elk_Exception($context['error_message'], false);
522
	}
523
524
	/**
525
	 * Insert data.
526
	 *
527
	 * @param string $method - options 'replace', 'ignore', 'insert'
528
	 * @param string $table
529
	 * @param mixed[] $columns
530
	 * @param mixed[] $data
531
	 * @param mixed[] $keys
532
	 * @param bool $disable_trans = false
533
	 * @param resource|null $connection = null
534
	 * @throws Elk_Exception
535
	 */
536
	public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
537
	{
538
		global $db_prefix;
539
540
		$connection = $connection === null ? $this->_connection : $connection;
541
542
		// With nothing to insert, simply return.
543
		if (empty($data))
544
			return;
545
546
		// Inserting data as a single row can be done as a single array.
547
		if (!is_array($data[array_rand($data)]))
548
			$data = array($data);
549
550
		// Replace the prefix holder with the actual prefix.
551
		$table = str_replace('{db_prefix}', $db_prefix, $table);
552
553
		$priv_trans = false;
554
		if ((count($data) > 1 || $method == 'replace') && !$this->_in_transaction && !$disable_trans)
555
		{
556
			$this->db_transaction('begin', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 540 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...
557
			$priv_trans = true;
558
		}
559
560
		// PostgreSQL doesn't support replace: we implement a MySQL-compatible behavior instead
561
		if ($method == 'replace')
562
		{
563
			$count = 0;
564
			$where = '';
565
			$db_replace_result = 0;
566
			foreach ($columns as $columnName => $type)
567
			{
568
				// Are we restricting the length?
569
				if (strpos($type, 'string-') !== false)
570
					$actualType = sprintf($columnName . ' = SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $count);
571
				else
572
					$actualType = sprintf($columnName . ' = {%1$s:%2$s}, ', $type, $count);
573
574
				// A key? That's what we were looking for.
575
				if (in_array($columnName, $keys))
576
					$where .= (empty($where) ? '' : ' AND ') . substr($actualType, 0, -2);
577
				$count++;
578
			}
579
580
			// Make it so.
581
			if (!empty($where) && !empty($data))
582
			{
583
				foreach ($data as $k => $entry)
584
				{
585
					$this->query('', '
586
						DELETE FROM ' . $table .
587
						' WHERE ' . $where,
588
						$entry, $connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 540 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...
589
					);
590
					$db_replace_result += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
591
				}
592
			}
593
		}
594
595
		if (!empty($data))
596
		{
597
			// Create the mold for a single row insert.
598
			$insertData = '(';
599 View Code Duplication
			foreach ($columns as $columnName => $type)
600
			{
601
				// Are we restricting the length?
602
				if (strpos($type, 'string-') !== false)
603
					$insertData .= sprintf('SUBSTRING({string:%1$s}, 1, ' . substr($type, 7) . '), ', $columnName);
604
				else
605
					$insertData .= sprintf('{%1$s:%2$s}, ', $type, $columnName);
606
			}
607
			$insertData = substr($insertData, 0, -2) . ')';
608
609
			// Create an array consisting of only the columns.
610
			$indexed_columns = array_keys($columns);
611
612
			// Here's where the variables are injected to the query.
613
			$insertRows = array();
614
			foreach ($data as $dataRow)
615
				$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 540 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...
616
617
			$inserted_results = 0;
618
			$skip_error = $method == 'ignore' || $table === $db_prefix . 'log_errors';
619
			foreach ($insertRows as $entry)
620
			{
621
				$this->_skip_error = $skip_error;
622
623
				// Do the insert.
624
				$this->query('', '
625
					INSERT INTO ' . $table . '("' . implode('", "', $indexed_columns) . '")
626
					VALUES
627
						' . $entry,
628
					array(
629
						'security_override' => true,
630
					),
631
					$connection
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 540 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...
632
				);
633
				$inserted_results += (!$this->_db_last_result ? 0 : pg_affected_rows($this->_db_last_result));
634
			}
635
			if (isset($db_replace_result))
636
				$this->_db_replace_result = $db_replace_result + $inserted_results;
637
		}
638
639
		if ($priv_trans)
640
			$this->db_transaction('commit', $connection);
0 ignored issues
show
Bug introduced by
It seems like $connection defined by $connection === null ? $...onnection : $connection on line 540 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...
641
	}
642
643
	/**
644
	 * Unescape an escaped string!
645
	 *
646
	 * @param string $string
647
	 */
648
	public function unescape_string($string)
649
	{
650
		return strtr($string, array('\'\'' => '\''));
651
	}
652
653
	/**
654
	 * Returns whether the database system supports ignore.
655
	 *
656
	 * @return false
657
	 */
658
	public function support_ignore()
659
	{
660
		return false;
661
	}
662
663
	/**
664
	 * Gets all the necessary INSERTs for the table named table_name.
665
	 * It goes in 250 row segments.
666
	 *
667
	 * @param string $tableName - the table to create the inserts for.
668
	 * @param bool $new_table
669
	 *
670
	 * @return string the query to insert the data back in, or an empty string if the table was empty.
671
	 * @throws Elk_Exception
672
	 */
673
	public function insert_sql($tableName, $new_table = false)
674
	{
675
		global $db_prefix;
676
677
		static $start = 0, $num_rows, $fields, $limit;
678
679 View Code Duplication
		if ($new_table)
680
		{
681
			$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
682
			$start = 0;
683
		}
684
685
		$data = '';
686
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
687
688
		// This will be handy...
689
		$crlf = "\r\n";
690
691
		$result = $this->query('', '
692
			SELECT *
693
			FROM ' . $tableName . '
694
			LIMIT ' . $start . ', ' . $limit,
695
			array(
696
				'security_override' => true,
697
			)
698
		);
699
700
		// The number of rows, just for record keeping and breaking INSERTs up.
701
		$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 691 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...
702
703
		if ($num_rows == 0)
704
			return '';
705
706
		if ($new_table)
707
		{
708
			$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 691 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...
709
			$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 691 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...
710
		}
711
712
		// Start it off with the basic INSERT INTO.
713
		$insert_msg = 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
714
715
		// Loop through each row.
716 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 691 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...
717
		{
718
			// Get the fields in this row...
719
			$field_list = array();
720
721
			foreach ($row as $key => $item)
722
			{
723
				// Try to figure out the type of each field. (NULL, number, or 'string'.)
724
				if (!isset($item))
725
					$field_list[] = 'NULL';
726
				elseif (is_numeric($item) && (int) $item == $item)
727
					$field_list[] = $item;
728
				else
729
					$field_list[] = '\'' . $this->escape_string($item) . '\'';
730
			}
731
732
			// 'Insert' the data.
733
			$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
734
		}
735
		$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 691 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...
736
737
		$data .= $crlf;
738
739
		$start += $limit;
740
741
		return $data;
742
	}
743
744
	/**
745
	 * Dumps the schema (CREATE) for a table.
746
	 *
747
	 * @param string $tableName - the table
748
	 *
749
	 * @return string - the CREATE statement as string
750
	 * @throws Elk_Exception
751
	 */
752
	public function db_table_sql($tableName)
753
	{
754
		global $db_prefix;
755
756
		$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
757
758
		// This will be needed...
759
		$crlf = "\r\n";
760
761
		// Start the create table...
762
		$schema_create = 'CREATE TABLE ' . $tableName . ' (' . $crlf;
763
		$index_create = '';
764
		$seq_create = '';
765
766
		// Find all the fields.
767
		$result = $this->query('', '
768
			SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
769
			FROM information_schema.columns
770
			WHERE table_name = {string:table}
771
			ORDER BY ordinal_position',
772
			array(
773
				'table' => $tableName,
774
			)
775
		);
776
		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 767 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...
777
		{
778
			if ($row['data_type'] == 'character varying')
779
				$row['data_type'] = 'varchar';
780
			elseif ($row['data_type'] == 'character')
781
				$row['data_type'] = 'char';
782
783
			if ($row['character_maximum_length'])
784
				$row['data_type'] .= '(' . $row['character_maximum_length'] . ')';
785
786
			// Make the CREATE for this column.
787
			$schema_create .= ' "' . $row['column_name'] . '" ' . $row['data_type'] . ($row['is_nullable'] != 'YES' ? ' NOT NULL' : '');
788
789
			// Add a default...?
790
			if (trim($row['column_default']) != '')
791
			{
792
				$schema_create .= ' default ' . $row['column_default'] . '';
793
794
				// Auto increment?
795
				if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
796
				{
797
					// Get to find the next variable first!
798
					$count_req = $this->query('', '
799
						SELECT MAX("{raw:column}")
800
						FROM {raw:table}',
801
						array(
802
							'column' => $row['column_name'],
803
							'table' => $tableName,
804
						)
805
					);
806
					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...
807
					$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 798 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...
808
809
					// Get the right bloody start!
810
					$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
811
				}
812
			}
813
814
			$schema_create .= ',' . $crlf;
815
		}
816
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 767 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...
817
818
		// Take off the last comma.
819
		$schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
820
821
		$result = $this->query('', '
822
			SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary, pg_get_indexdef(i.indexrelid) AS inddef
823
			FROM pg_class AS c
824
				INNER JOIN pg_index AS i ON (i.indrelid = c.oid)
825
				INNER JOIN pg_class AS c2 ON (c2.oid = i.indexrelid)
826
			WHERE c.relname = {string:table}',
827
			array(
828
				'table' => $tableName,
829
			)
830
		);
831
832
		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 821 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...
833
		{
834
			if ($row['is_primary'])
835
			{
836
				if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
837
					continue;
838
839
				$index_create .= $crlf . 'ALTER TABLE ' . $tableName . ' ADD PRIMARY KEY ("' . $matches[1] . '");';
840
			}
841
			else
842
				$index_create .= $crlf . $row['inddef'] . ';';
843
		}
844
		$this->free_result($result);
0 ignored issues
show
Bug introduced by
It seems like $result defined by $this->query('', ' SE...'table' => $tableName)) on line 821 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...
845
846
		// Finish it off!
847
		$schema_create .= $crlf . ');';
848
849
		return $seq_create . $schema_create . $index_create;
850
	}
851
852
	/**
853
	 * {@inheritdoc}
854
	 */
855
	public function db_list_tables($db_name_str = false, $filter = false)
856
	{
857
		$request = $this->query('', '
858
			SELECT tablename
859
			FROM pg_tables
860
			WHERE schemaname = {string:schema_public}' . ($filter === false ? '' : '
861
				AND tablename LIKE {string:filter}') . '
862
			ORDER BY tablename',
863
			array(
864
				'schema_public' => 'public',
865
				'filter' => $filter,
866
			)
867
		);
868
		$tables = array();
869
		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 857 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...
870
			$tables[] = $row[0];
871
		$this->free_result($request);
0 ignored issues
show
Bug introduced by
It seems like $request defined by $this->query('', ' SE..., 'filter' => $filter)) on line 857 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...
872
873
		return $tables;
874
	}
875
876
	/**
877
	 * Backup $table to $backup_table.
878
	 *
879
	 * @param string $table
880
	 * @param string $backup_table
881
	 * @throws Elk_Exception
882
	 */
883
	public function db_backup_table($table, $backup_table)
884
	{
885
		global $db_prefix;
886
887
		$table = str_replace('{db_prefix}', $db_prefix, $table);
888
889
		// Do we need to drop it first?
890
		$db_table = db_table();
891
		$db_table->db_drop_table($backup_table);
892
893
		// @todo Should we create backups of sequences as well?
894
		$this->query('', '
895
			CREATE TABLE {raw:backup_table}
896
			(
897
				LIKE {raw:table}
898
				INCLUDING DEFAULTS
899
			)',
900
			array(
901
				'backup_table' => $backup_table,
902
				'table' => $table,
903
			)
904
		);
905
906
		$this->query('', '
907
			INSERT INTO {raw:backup_table}
908
			SELECT * FROM {raw:table}',
909
			array(
910
				'backup_table' => $backup_table,
911
				'table' => $table,
912
			)
913
		);
914
	}
915
916
	/**
917
	 * Get the server version number.
918
	 *
919
	 * @return string - the version
920
	 */
921
	public function db_server_version()
922
	{
923
		$version = pg_version();
924
925
		return $version['server'];
926
	}
927
928
	/**
929
	 * Get the name (title) of the database system.
930
	 *
931
	 * @return string
932
	 */
933
	public function db_title()
934
	{
935
		return 'PostgreSQL';
936
	}
937
938
	/**
939
	 * Whether the database system is case sensitive.
940
	 *
941
	 * @return boolean
942
	 */
943
	public function db_case_sensitive()
944
	{
945
		return true;
946
	}
947
948
	/**
949
	 * Quotes identifiers for replacement__callback.
950
	 *
951
	 * @param mixed $replacement
952
	 * @return string
953
	 * @throws Elk_Exception
954
	 */
955 View Code Duplication
	protected function _replaceIdentifier($replacement)
956
	{
957
		if (preg_match('~[a-z_][0-9,a-z,A-Z$_]{0,60}~', $replacement) !== 1)
958
		{
959
			$this->error_backtrace('Wrong value type sent to the database. Invalid identifier used. (' . $replacement . ')', '', E_USER_ERROR, __FILE__, __LINE__);
960
		}
961
962
		return '"' . $replacement . '"';
963
	}
964
965
	/**
966
	 * Escape string for the database input
967
	 *
968
	 * @param string $string
969
	 */
970
	public function escape_string($string)
971
	{
972
		return pg_escape_string($string);
973
	}
974
975
	/**
976
	 * Fetch next result as association.
977
	 *
978
	 * @param resource $request
979
	 * @param int|bool $counter = false
980
	 */
981 View Code Duplication
	public function fetch_assoc($request, $counter = false)
982
	{
983
		global $db_row_count;
984
985
		if ($counter !== false)
986
			return pg_fetch_assoc($request, $counter);
987
988
		// Reset the row counter...
989
		if (!isset($db_row_count[(int) $request]))
990
			$db_row_count[(int) $request] = 0;
991
992
		// Return the right row.
993
		return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
994
	}
995
996
	/**
997
	 * Return server info.
998
	 *
999
	 * @return string
1000
	 */
1001
	public function db_server_info()
1002
	{
1003
		// give info on client! we use it in install and upgrade and such things.
1004
		$version = pg_version();
1005
1006
		return $version['client'];
1007
	}
1008
1009
	/**
1010
	 * Return client version.
1011
	 *
1012
	 * @return string - the version
1013
	 */
1014
	public function db_client_version()
1015
	{
1016
		$version = pg_version();
1017
1018
		return $version['client'];
1019
	}
1020
1021
	/**
1022
	 * Dummy function really. Doesn't do anything on PostgreSQL.
1023
	 *
1024
	 * @param string|null $db_name = null
1025
	 * @param resource|null $connection = null
1026
	 *
1027
	 * @return boolean
1028
	 */
1029
	public function select_db($db_name = null, $connection = null)
1030
	{
1031
		return true;
1032
	}
1033
1034
	/**
1035
	 * Returns a reference to the existing instance
1036
	 */
1037
	public static function db()
1038
	{
1039
		return self::$_db;
1040
	}
1041
1042
	/**
1043
	 * Finds out if the connection is still valid.
1044
	 *
1045
	 * @param postgre|null $connection = null
1046
	 */
1047
	public function validConnection($connection = null)
1048
	{
1049
		return is_resource($connection);
1050
	}
1051
}
1052