Completed
Push — release-2.1 ( e7eefc...20ec18 )
by Colin
07:17
created

DbExtra-mysql.php ➔ smf_db_get_engine()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 25
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file contains rarely used extended database functionality.
5
 *
6
 * Simple Machines Forum (SMF)
7
 *
8
 * @package SMF
9
 * @author Simple Machines http://www.simplemachines.org
10
 * @copyright 2017 Simple Machines and individual contributors
11
 * @license http://www.simplemachines.org/about/smf/license.php BSD
12
 *
13
 * @version 2.1 Beta 4
14
 */
15
16
if (!defined('SMF'))
17
	die('No direct access...');
18
19
/**
20
 * Add the functions implemented in this file to the $smcFunc array.
21
 */
22
function db_extra_init()
23
{
24
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
25
26
	if (!isset($smcFunc['db_backup_table']) || $smcFunc['db_backup_table'] != 'smf_db_backup_table')
27
		$smcFunc += array(
28
			'db_backup_table' => 'smf_db_backup_table',
29
			'db_optimize_table' => 'smf_db_optimize_table',
30
			'db_table_sql' => 'smf_db_table_sql',
31
			'db_list_tables' => 'smf_db_list_tables',
32
			'db_get_version' => 'smf_db_get_version',
33
			'db_get_vendor' => 'smf_db_get_vendor',
34
		);
35
}
36
37
/**
38
 * Backup $table to $backup_table.
39
 * @param string $table The name of the table to backup
40
 * @param string $backup_table The name of the backup table for this table
41
 * @return resource -the request handle to the table creation query
0 ignored issues
show
Documentation introduced by
Should the return type not be resource|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
42
 */
43
function smf_db_backup_table($table, $backup_table)
44
{
45
	global $smcFunc, $db_prefix;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
46
47
	$table = str_replace('{db_prefix}', $db_prefix, $table);
48
49
	// First, get rid of the old table.
50
	$smcFunc['db_query']('', '
51
		DROP TABLE IF EXISTS {raw:backup_table}',
52
		array(
53
			'backup_table' => $backup_table,
54
		)
55
	);
56
57
	// Can we do this the quick way?
58
	$result = $smcFunc['db_query']('', '
59
		CREATE TABLE {raw:backup_table} LIKE {raw:table}',
60
		array(
61
			'backup_table' => $backup_table,
62
			'table' => $table
63
	));
64
	// If this failed, we go old school.
65
	if ($result)
66
	{
67
		$request = $smcFunc['db_query']('', '
68
			INSERT INTO {raw:backup_table}
69
			SELECT *
70
			FROM {raw:table}',
71
			array(
72
				'backup_table' => $backup_table,
73
				'table' => $table
74
			));
75
76
		// Old school or no school?
77
		if ($request)
78
			return $request;
79
	}
80
81
	// At this point, the quick method failed.
82
	$result = $smcFunc['db_query']('', '
83
		SHOW CREATE TABLE {raw:table}',
84
		array(
85
			'table' => $table,
86
		)
87
	);
88
	list (, $create) = $smcFunc['db_fetch_row']($result);
89
	$smcFunc['db_free_result']($result);
90
91
	$create = preg_split('/[\n\r]/', $create);
92
93
	$auto_inc = '';
94
	// Default engine type.
95
	$engine = 'MyISAM';
96
	$charset = '';
97
	$collate = '';
98
99
	foreach ($create as $k => $l)
100
	{
101
		// Get the name of the auto_increment column.
102
		if (strpos($l, 'auto_increment'))
103
			$auto_inc = trim($l);
104
105
		// For the engine type, see if we can work out what it is.
106
		if (strpos($l, 'ENGINE') !== false || strpos($l, 'TYPE') !== false)
107
		{
108
			// Extract the engine type.
109
			preg_match('~(ENGINE|TYPE)=(\w+)(\sDEFAULT)?(\sCHARSET=(\w+))?(\sCOLLATE=(\w+))?~', $l, $match);
110
111
			if (!empty($match[1]))
112
				$engine = $match[1];
113
114
			if (!empty($match[2]))
115
				$engine = $match[2];
116
117
			if (!empty($match[5]))
118
				$charset = $match[5];
119
120
			if (!empty($match[7]))
121
				$collate = $match[7];
122
		}
123
124
		// Skip everything but keys...
125
		if (strpos($l, 'KEY') === false)
126
			unset($create[$k]);
127
	}
128
129
	if (!empty($create))
130
		$create = '(
131
			' . implode('
132
			', $create) . ')';
133
	else
134
		$create = '';
135
136
	$request = $smcFunc['db_query']('', '
137
		CREATE TABLE {raw:backup_table} {raw:create}
138
		ENGINE={raw:engine}' . (empty($charset) ? '' : ' CHARACTER SET {raw:charset}' . (empty($collate) ? '' : ' COLLATE {raw:collate}')) . '
139
		SELECT *
140
		FROM {raw:table}',
141
		array(
142
			'backup_table' => $backup_table,
143
			'table' => $table,
144
			'create' => $create,
145
			'engine' => $engine,
146
			'charset' => empty($charset) ? '' : $charset,
147
			'collate' => empty($collate) ? '' : $collate,
148
		)
149
	);
150
151
	if ($auto_inc != '')
152
	{
153
		if (preg_match('~\`(.+?)\`\s~', $auto_inc, $match) != 0 && substr($auto_inc, -1, 1) == ',')
154
			$auto_inc = substr($auto_inc, 0, -1);
155
156
		$smcFunc['db_query']('', '
157
			ALTER TABLE {raw:backup_table}
158
			CHANGE COLUMN {raw:column_detail} {raw:auto_inc}',
159
			array(
160
				'backup_table' => $backup_table,
161
				'column_detail' => $match[1],
162
				'auto_inc' => $auto_inc,
163
			)
164
		);
165
	}
166
167
	return $request;
168
}
169
170
/**
171
 * This function optimizes a table.
172
 * @param string $table The table to be optimized
173
 * @return int How much space was gained
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|double?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
174
 */
175
function smf_db_optimize_table($table)
176
{
177
	global $smcFunc, $db_prefix;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
178
179
	$table = str_replace('{db_prefix}', $db_prefix, $table);
180
181
	// Get how much overhead there is.
182
	$request = $smcFunc['db_query']('', '
183
			SHOW TABLE STATUS LIKE {string:table_name}',
184
			array(
185
				'table_name' => str_replace('_', '\_', $table),
186
			)
187
		);
188
	$row = $smcFunc['db_fetch_assoc']($request);
189
	$smcFunc['db_free_result']($request);
190
191
	$data_before = isset($row['Data_free']) ? $row['Data_free'] : 0;
192
	$request = $smcFunc['db_query']('', '
193
			OPTIMIZE TABLE `{raw:table}`',
194
			array(
195
				'table' => $table,
196
			)
197
		);
198
	if (!$request)
199
		return -1;
200
201
	// How much left?
202
	$request = $smcFunc['db_query']('', '
203
			SHOW TABLE STATUS LIKE {string:table}',
204
			array(
205
				'table' => str_replace('_', '\_', $table),
206
			)
207
		);
208
	$row = $smcFunc['db_fetch_assoc']($request);
209
	$smcFunc['db_free_result']($request);
210
211
	$total_change = isset($row['Data_free']) && $data_before > $row['Data_free'] ? $data_before / 1024 : 0;
212
213
	return $total_change;
214
}
215
216
/**
217
 * This function lists all tables in the database.
218
 * The listing could be filtered according to $filter.
219
 *
220
 * @param string|boolean $db string The database name or false to use the current DB
221
 * @param string|boolean $filter String to filter by or false to list all tables
222
 * @return array An array of table names
223
 */
224
function smf_db_list_tables($db = false, $filter = false)
0 ignored issues
show
Unused Code introduced by
The parameter $db 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...
Comprehensibility introduced by
Avoid variables with short names like $db. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
225
{
226
	global $db_name, $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
227
228
	$db = $db == false ? $db_name : $db;
229
	$db = trim($db);
230
	$filter = $filter == false ? '' : ' LIKE \'' . $filter . '\'';
231
232
	$request = $smcFunc['db_query']('', '
233
		SHOW TABLES
234
		FROM `{raw:db}`
235
		{raw:filter}',
236
		array(
237
			'db' => $db[0] == '`' ? strtr($db, array('`' => '')) : $db,
238
			'filter' => $filter,
239
		)
240
	);
241
	$tables = array();
242
	while ($row = $smcFunc['db_fetch_row']($request))
243
		$tables[] = $row[0];
244
	$smcFunc['db_free_result']($request);
245
246
	return $tables;
247
}
248
249
/**
250
 * Dumps the schema (CREATE) for a table.
251
 * @todo why is this needed for?
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
252
 * @param string $tableName The name of the table
253
 * @return string The "CREATE TABLE" SQL string for this table
254
 */
255
function smf_db_table_sql($tableName)
256
{
257
	global $smcFunc, $db_prefix;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
258
259
	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
260
261
	// This will be needed...
262
	$crlf = "\r\n";
263
264
	// Drop it if it exists.
265
	$schema_create = 'DROP TABLE IF EXISTS `' . $tableName . '`;' . $crlf . $crlf;
266
267
	// Start the create table...
268
	$schema_create .= 'CREATE TABLE `' . $tableName . '` (' . $crlf;
269
270
	// Find all the fields.
271
	$result = $smcFunc['db_query']('', '
272
		SHOW FIELDS
273
		FROM `{raw:table}`',
274
		array(
275
			'table' => $tableName,
276
		)
277
	);
278
	while ($row = $smcFunc['db_fetch_assoc']($result))
279
	{
280
		// Make the CREATE for this column.
281
		$schema_create .= ' `' . $row['Field'] . '` ' . $row['Type'] . ($row['Null'] != 'YES' ? ' NOT NULL' : '');
282
283
		// Add a default...?
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
284
		if (!empty($row['Default']) || $row['Null'] !== 'YES')
285
		{
286
			// Make a special case of auto-timestamp.
287
			if ($row['Default'] == 'CURRENT_TIMESTAMP')
288
				$schema_create .= ' /*!40102 NOT NULL default CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP */';
289
			// Text shouldn't have a default.
290
			elseif ($row['Default'] !== null)
291
			{
292
				// If this field is numeric the default needs no escaping.
293
				$type = strtolower($row['Type']);
294
				$isNumericColumn = strpos($type, 'int') !== false || strpos($type, 'bool') !== false || strpos($type, 'bit') !== false || strpos($type, 'float') !== false || strpos($type, 'double') !== false || strpos($type, 'decimal') !== false;
295
296
				$schema_create .= ' default ' . ($isNumericColumn ? $row['Default'] : '\'' . $smcFunc['db_escape_string']($row['Default']) . '\'');
297
			}
298
		}
299
300
		// And now any extra information. (such as auto_increment.)
301
		$schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf;
302
	}
303
	$smcFunc['db_free_result']($result);
304
305
	// Take off the last comma.
306
	$schema_create = substr($schema_create, 0, -strlen($crlf) - 1);
307
308
	// Find the keys.
309
	$result = $smcFunc['db_query']('', '
310
		SHOW KEYS
311
		FROM `{raw:table}`',
312
		array(
313
			'table' => $tableName,
314
		)
315
	);
316
	$indexes = array();
317
	while ($row = $smcFunc['db_fetch_assoc']($result))
318
	{
319
		// IS this a primary key, unique index, or regular index?
320
		$row['Key_name'] = $row['Key_name'] == 'PRIMARY' ? 'PRIMARY KEY' : (empty($row['Non_unique']) ? 'UNIQUE ' : ($row['Comment'] == 'FULLTEXT' || (isset($row['Index_type']) && $row['Index_type'] == 'FULLTEXT') ? 'FULLTEXT ' : 'KEY ')) . '`' . $row['Key_name'] . '`';
321
322
		// Is this the first column in the index?
323
		if (empty($indexes[$row['Key_name']]))
324
			$indexes[$row['Key_name']] = array();
325
326
		// A sub part, like only indexing 15 characters of a varchar.
327
		if (!empty($row['Sub_part']))
328
			$indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`(' . $row['Sub_part'] . ')';
329
		else
330
			$indexes[$row['Key_name']][$row['Seq_in_index']] = '`' . $row['Column_name'] . '`';
331
	}
332
	$smcFunc['db_free_result']($result);
333
334
	// Build the CREATEs for the keys.
335
	foreach ($indexes as $keyname => $columns)
336
	{
337
		// Ensure the columns are in proper order.
338
		ksort($columns);
339
340
		$schema_create .= ',' . $crlf . ' ' . $keyname . ' (' . implode($columns, ', ') . ')';
341
	}
342
343
	// Now just get the comment and engine... (MyISAM, etc.)
344
	$result = $smcFunc['db_query']('', '
345
		SHOW TABLE STATUS
346
		LIKE {string:table}',
347
		array(
348
			'table' => strtr($tableName, array('_' => '\\_', '%' => '\\%')),
349
		)
350
	);
351
	$row = $smcFunc['db_fetch_assoc']($result);
352
	$smcFunc['db_free_result']($result);
353
354
	// Probably MyISAM.... and it might have a comment.
355
	$schema_create .= $crlf . ') ENGINE=' . $row['Engine'] . ($row['Comment'] != '' ? ' COMMENT="' . $row['Comment'] . '"' : '');
356
357
	return $schema_create;
358
}
359
360
/**
361
 *  Get the version number.
362
 *  @return string The version
363
 */
364
function smf_db_get_version()
365
{
366
	static $ver;
367
368
	if (!empty($ver))
369
		return $ver;
370
371
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
372
373
	$request = $smcFunc['db_query']('', '
374
		SELECT VERSION()',
375
		array(
376
		)
377
	);
378
	list ($ver) = $smcFunc['db_fetch_row']($request);
379
	$smcFunc['db_free_result']($request);
380
381
	return $ver;
382
}
383
384
/**
385
 * Figures out if we are using MySQL, Percona or MariaDB
386
 *
387
 * @return string The database engine we are using
388
*/
389
function smf_db_get_vendor()
390
{
391
	global $smcFunc;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
392
	static $db_type;
393
394
	if (!empty($db_type))
395
		return $db_type;
396
397
	$request = $smcFunc['db_query']('', 
398
				'SELECT VARIABLE_VALUE
399
				 FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES
400
				 WHERE variable_name = {string:var_name}',
401
				array(
402
					'var_name' => 'VERSION_COMMENT',
403
				)
404
			);
405
	list ($comment) = $smcFunc['db_fetch_row']($request);
406
	$smcFunc['db_free_result']($request);
407
408
	// Skip these if we don't have a comment.
409
	if (!empty($comment))
410
	{
411
		if (stripos($comment, 'percona') !== false)
412
			return 'Percona';
413
		if (stripos($comment, 'mariadb') !== false)
414
			return 'MariaDB';
415
	}
416
	else
417
		return 'fail';
418
419
	return 'MySQL';
420
}
421
422
?>