Completed
Push — release-2.1 ( 6f6d35...abeae7 )
by Mathias
08:46
created

Sources/DbPackages-postgresql.php (7 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
/**
4
 * This file contains database functionality specifically designed for packages (mods) to utilize.
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 file functions to the $smcFunc array.
21
 */
22 View Code Duplication
function db_packages_init()
23
{
24
	global $smcFunc, $reservedTables, $db_package_log, $db_prefix;
25
26
	if (!isset($smcFunc['db_create_table']) || $smcFunc['db_create_table'] != 'smf_db_create_table')
27
	{
28
		$smcFunc += array(
29
			'db_add_column' => 'smf_db_add_column',
30
			'db_add_index' => 'smf_db_add_index',
31
			'db_calculate_type' => 'smf_db_calculate_type',
32
			'db_change_column' => 'smf_db_change_column',
33
			'db_create_table' => 'smf_db_create_table',
34
			'db_drop_table' => 'smf_db_drop_table',
35
			'db_table_structure' => 'smf_db_table_structure',
36
			'db_list_columns' => 'smf_db_list_columns',
37
			'db_list_indexes' => 'smf_db_list_indexes',
38
			'db_remove_column' => 'smf_db_remove_column',
39
			'db_remove_index' => 'smf_db_remove_index',
40
		);
41
		$db_package_log = array();
42
	}
43
44
	// We setup an array of SMF tables we can't do auto-remove on - in case a mod writer cocks it up!
45
	$reservedTables = array('admin_info_files', 'approval_queue', 'attachments', 'ban_groups', 'ban_items',
46
		'board_permissions', 'boards', 'calendar', 'calendar_holidays', 'categories',
47
		'custom_fields', 'group_moderators', 'log_actions', 'log_activity', 'log_banned', 'log_boards',
48
		'log_digest', 'log_errors', 'log_floodcontrol', 'log_group_requests', 'log_mark_read',
49
		'log_notify', 'log_online', 'log_packages', 'log_polls', 'log_reported', 'log_reported_comments',
50
		'log_scheduled_tasks', 'log_search_messages', 'log_search_results', 'log_search_subjects',
51
		'log_search_topics', 'log_topics', 'mail_queue', 'membergroups', 'members', 'message_icons',
52
		'messages', 'moderators', 'package_servers', 'permission_profiles', 'permissions', 'personal_messages',
53
		'pm_recipients', 'poll_choices', 'polls', 'scheduled_tasks', 'sessions', 'settings', 'smileys',
54
		'themes', 'topics');
55
	foreach ($reservedTables as $k => $table_name)
56
		$reservedTables[$k] = strtolower($db_prefix . $table_name);
57
58
	// We in turn may need the extra stuff.
59
	db_extend('extra');
60
}
61
62
/**
63
 * This function can be used to create a table without worrying about schema
64
 *  compatibilities across supported database systems.
65
 *  - If the table exists will, by default, do nothing.
66
 *  - Builds table with columns as passed to it - at least one column must be sent.
67
 *  The columns array should have one sub-array for each column - these sub arrays contain:
68
 *  	'name' = Column name
69
 *  	'type' = Type of column - values from (smallint, mediumint, int, text, varchar, char, tinytext, mediumtext, largetext)
70
 *  	'size' => Size of column (If applicable) - for example 255 for a large varchar, 10 for an int etc.
71
 *  		If not set SMF will pick a size.
72
 *  	- 'default' = Default value - do not set if no default required.
73
 *  	- 'null' => Can it be null (true or false) - if not set default will be false.
74
 *  	- 'auto' => Set to true to make it an auto incrementing column. Set to a numerical value to set from what
75
 *  		 it should begin counting.
76
 *  - Adds indexes as specified within indexes parameter. Each index should be a member of $indexes. Values are:
77
 *  	- 'name' => Index name (If left empty SMF will generate).
78
 *  	- 'type' => Type of index. Choose from 'primary', 'unique' or 'index'. If not set will default to 'index'.
79
 *  	- 'columns' => Array containing columns that form part of key - in the order the index is to be created.
80
 *  - parameters: (None yet)
81
 *  - if_exists values:
82
 *  	- 'ignore' will do nothing if the table exists. (And will return true)
83
 *  	- 'overwrite' will drop any existing table of the same name.
84
 *  	- 'error' will return false if the table already exists.
85
 *
86
 * @param string $table_name The name of the table to create
87
 * @param array $columns An array of column info in the specified format
88
 * @param array $indexes An array of index info in the specified format
89
 * @param array $parameters Currently not used
90
 * @param string $if_exists What to do if the table exists.
91
 * @param string $error
92
 */
93
function smf_db_create_table($table_name, $columns, $indexes = array(), $parameters = array(), $if_exists = 'ignore', $error = 'fatal')
94
{
95
	global $reservedTables, $smcFunc, $db_package_log, $db_prefix;
96
97
	// Strip out the table name, we might not need it in some cases
98
	$real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
99
100
	// With or without the database name, the fullname looks like this.
101
	$full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
102
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
103
104
	// First - no way do we touch SMF tables.
105
	if (in_array(strtolower($table_name), $reservedTables))
106
		return false;
107
108
	// Log that we'll want to remove this on uninstall.
109
	$db_package_log[] = array('remove_table', $table_name);
110
111
	// This... my friends... is a function in a half - let's start by checking if the table exists!
112
	$tables = $smcFunc['db_list_tables']();
113 View Code Duplication
	if (in_array($full_table_name, $tables))
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114
	{
115
		// This is a sad day... drop the table? If not, return false (error) by default.
116
		if ($if_exists == 'overwrite')
117
			$smcFunc['db_drop_table']($table_name);
118
		else
119
			return $if_exists == 'ignore';
120
	}
121
122
	// If we've got this far - good news - no table exists. We can build our own!
123
	$smcFunc['db_transaction']('begin');
124
	$table_query = 'CREATE TABLE ' . $table_name . "\n" . '(';
125
	foreach ($columns as $column)
126
	{
127
		// If we have an auto increment do it!
128
		if (!empty($column['auto']))
129
		{
130
			$smcFunc['db_query']('', '
131
				DROP SEQUENCE IF EXISTS ' . $table_name . '_seq',
132
				array(
133
					'security_override' => true,
134
				)
135
			);
136
						
137
			$smcFunc['db_query']('', '
138
				CREATE SEQUENCE ' . $table_name . '_seq',
139
				array(
140
					'security_override' => true,
141
				)
142
			);
143
			$default = 'default nextval(\'' . $table_name . '_seq\')';
144
		}
145 View Code Duplication
		elseif (isset($column['default']) && $column['default'] !== null)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
146
			$default = 'default \'' . $smcFunc['db_escape_string']($column['default']) . '\'';
147
		else
148
			$default = '';
149
150
		// Sort out the size...
151
		$column['size'] = isset($column['size']) && is_numeric($column['size']) ? $column['size'] : null;
152
		list ($type, $size) = $smcFunc['db_calculate_type']($column['type'], $column['size']);
153
		if ($size !== null)
154
			$type = $type . '(' . $size . ')';
155
156
		// Now just put it together!
157
		$table_query .= "\n\t\"" . $column['name'] . '" ' . $type . ' ' . (!empty($column['null']) ? '' : 'NOT NULL') . ' ' . $default . ',';
158
	}
159
160
	// Loop through the indexes a sec...
161
	$index_queries = array();
162
	foreach ($indexes as $index)
163
	{
164
		$columns = implode(',', $index['columns']);
165
166
		// Primary goes in the table...
167
		if (isset($index['type']) && $index['type'] == 'primary')
168
			$table_query .= "\n\t" . 'PRIMARY KEY (' . implode(',', $index['columns']) . '),';
169
		else
170
		{
171
			if (empty($index['name']))
172
				$index['name'] = implode('_', $index['columns']);
173
			$index_queries[] = 'CREATE ' . (isset($index['type']) && $index['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $table_name . '_' . $index['name'] . ' ON ' . $table_name . ' (' . $columns . ')';
174
		}
175
	}
176
177
	// No trailing commas!
178 View Code Duplication
	if (substr($table_query, -1) == ',')
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
		$table_query = substr($table_query, 0, -1);
180
181
	$table_query .= ')';
182
183
	// Create the table!
184
	$smcFunc['db_query']('', $table_query,
185
		array(
186
			'security_override' => true,
187
		)
188
	);
189
	// And the indexes...
190
	foreach ($index_queries as $query)
191
		$smcFunc['db_query']('', $query,
192
		array(
193
			'security_override' => true,
194
		)
195
	);
196
197
	// Go, go power rangers!
198
	$smcFunc['db_transaction']('commit');
199
200
	return true;
201
}
202
203
/**
204
 * Drop a table and its associated sequences.
205
 *
206
 * @param string $table_name The name of the table to drop
207
 * @param array $parameters Not used at the moment
208
 * @param string $error
209
 * @return boolean Whether or not the operation was successful
210
 */
211
function smf_db_drop_table($table_name, $parameters = array(), $error = 'fatal')
212
{
213
	global $reservedTables, $smcFunc, $db_prefix;
214
215
	// After stripping away the database name, this is what's left.
216
	$real_prefix = preg_match('~^("?)(.+?)\\1\\.(.*?)$~', $db_prefix, $match) === 1 ? $match[3] : $db_prefix;
217
218
	// Get some aliases.
219
	$full_table_name = str_replace('{db_prefix}', $real_prefix, $table_name);
220
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
221
222
	// God no - dropping one of these = bad.
223
	if (in_array(strtolower($table_name), $reservedTables))
224
		return false;
225
226
	// Does it exist?
227
	if (in_array($full_table_name, $smcFunc['db_list_tables']()))
228
	{
229
		// We can then drop the table.
230
		$smcFunc['db_transaction']('begin');
231
232
		// the table
233
		$table_query = 'DROP TABLE ' . $table_name;
234
235
		// and the assosciated sequence, if any
236
		$sequence_query = 'DROP SEQUENCE IF EXISTS ' . $table_name . '_seq';
237
238
		// drop them
239
		$smcFunc['db_query']('',
240
			$table_query,
241
			array(
242
				'security_override' => true,
243
			)
244
		);
245
		$smcFunc['db_query']('',
246
			$sequence_query,
247
			array(
248
				'security_override' => true,
249
			)
250
		);
251
252
		$smcFunc['db_transaction']('commit');
253
254
		return true;
255
	}
256
257
	// Otherwise do 'nout.
258
	return false;
259
}
260
261
/**
262
 * This function adds a column.
263
 *
264
 * @param string $table_name The name of the table to add the column to
265
 * @param array $column_info An array of column info (see {@link smf_db_create_table()})
266
 * @param array $parameters Not used?
267
 * @param string $if_exists What to do if the column exists. If 'update', column is updated.
268
 * @param string $error
269
 * @return boolean Whether or not the operation was successful
270
 */
271
function smf_db_add_column($table_name, $column_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
272
{
273
	global $smcFunc, $db_package_log, $db_prefix;
274
275
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
276
277
	// Log that we will want to uninstall this!
278
	$db_package_log[] = array('remove_column', $table_name, $column_info['name']);
279
280
	// Does it exist - if so don't add it again!
281
	$columns = $smcFunc['db_list_columns']($table_name, false);
282 View Code Duplication
	foreach ($columns as $column)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
283
		if ($column == $column_info['name'])
284
		{
285
			// If we're going to overwrite then use change column.
286
			if ($if_exists == 'update')
287
				return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
288
			else
289
				return false;
290
		}
291
292
	// Get the specifics...
293
	$column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;
294
	list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
295
	if ($size !== null)
296
		$type = $type . '(' . $size . ')';
297
298
	// Now add the thing!
299
	$query = '
300
		ALTER TABLE ' . $table_name . '
301
		ADD COLUMN ' . $column_info['name'] . ' ' . $type;
302
	$smcFunc['db_query']('', $query,
303
		array(
304
			'security_override' => true,
305
		)
306
	);
307
308
	// If there's more attributes they need to be done via a change on PostgreSQL.
309
	unset($column_info['type'], $column_info['size']);
310
311
	if (count($column_info) != 1)
312
		return $smcFunc['db_change_column']($table_name, $column_info['name'], $column_info);
313
	else
314
		return true;
315
}
316
317
/**
318
 * Removes a column.
319
 *
320
 * @param string $table_name The name of the table to drop the column from
321
 * @param string $column_name The name of the column to drop
322
 * @param array $parameters Not used?
323
 * @param string $error
324
 * @return boolean Whether or not the operation was successful
325
 */
326
function smf_db_remove_column($table_name, $column_name, $parameters = array(), $error = 'fatal')
327
{
328
	global $smcFunc, $db_prefix;
329
330
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
331
332
	// Does it exist?
333
	$columns = $smcFunc['db_list_columns']($table_name, true);
334
	foreach ($columns as $column)
335
		if ($column['name'] == $column_name)
336
		{
337
			// If there is an auto we need remove it!
338
			if ($column['auto'])
339
				$smcFunc['db_query']('', '
340
					DROP SEQUENCE IF EXISTS ' . $table_name . '_seq',
341
					array(
342
						'security_override' => true,
343
					)
344
				);
345
346
			$smcFunc['db_query']('', '
347
				ALTER TABLE ' . $table_name . '
348
				DROP COLUMN ' . $column_name,
349
				array(
350
					'security_override' => true,
351
				)
352
			);
353
354
			return true;
355
		}
356
357
	// If here we didn't have to work - joy!
358
	return false;
359
}
360
361
/**
362
 * Change a column.
363
 *
364
 * @param string $table_name The name of the table this column is in
365
 * @param string $old_column The name of the column we want to change
366
 * @param array $column_info An array of info about the "new" column definition (see {@link smf_db_create_table()})
367
 * @return bool
368
 */
369
function smf_db_change_column($table_name, $old_column, $column_info)
370
{
371
	global $smcFunc, $db_prefix;
372
373
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
374
375
	// Check it does exist!
376
	$columns = $smcFunc['db_list_columns']($table_name, true);
377
	$old_info = null;
378
	foreach ($columns as $column)
379
		if ($column['name'] == $old_column)
380
			$old_info = $column;
381
382
	// Nothing?
383
	if ($old_info == null)
384
		return false;
385
386
	// Now we check each bit individually and ALTER as required.
387
	if (isset($column_info['name']) && $column_info['name'] != $old_column)
388
	{
389
		$smcFunc['db_query']('', '
390
			ALTER TABLE ' . $table_name . '
391
			RENAME COLUMN ' . $old_column . ' TO ' . $column_info['name'],
392
			array(
393
				'security_override' => true,
394
			)
395
		);
396
	}
397
	// Different default?
398 View Code Duplication
	if (isset($column_info['default']) && $column_info['default'] != $old_info['default'])
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
399
	{
400
		$action = $column_info['default'] !== null ? 'SET DEFAULT \'' . $smcFunc['db_escape_string']($column_info['default']) . '\'' : 'DROP DEFAULT';
401
		$smcFunc['db_query']('', '
402
			ALTER TABLE ' . $table_name . '
403
			ALTER COLUMN ' . $column_info['name'] . ' ' . $action,
404
			array(
405
				'security_override' => true,
406
			)
407
		);
408
	}
409
	// Is it null - or otherwise?
410
	if (isset($column_info['null']) && $column_info['null'] != $old_info['null'])
411
	{
412
		$action = $column_info['null'] ? 'DROP' : 'SET';
413
		$smcFunc['db_transaction']('begin');
414 View Code Duplication
		if (!$column_info['null'])
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
415
		{
416
			// We have to set it to something if we are making it NOT NULL. And we must comply with the current column format.
417
			$setTo = isset($column_info['default']) ? $column_info['default'] : (strpos($old_info['type'], 'int') !== false ? 0 : '');
418
			$smcFunc['db_query']('', '
419
				UPDATE ' . $table_name . '
420
				SET ' . $column_info['name'] . ' = \'' . $setTo . '\'
421
				WHERE ' . $column_info['name'] . ' IS NULL',
422
				array(
423
					'security_override' => true,
424
				)
425
			);
426
		}
427
		$smcFunc['db_query']('', '
428
			ALTER TABLE ' . $table_name . '
429
			ALTER COLUMN ' . $column_info['name'] . ' ' . $action . ' NOT NULL',
430
			array(
431
				'security_override' => true,
432
			)
433
		);
434
		$smcFunc['db_transaction']('commit');
435
	}
436
	// What about a change in type?
437
	if (isset($column_info['type']) && ($column_info['type'] != $old_info['type'] || (isset($column_info['size']) && $column_info['size'] != $old_info['size'])))
438
	{
439
		$column_info['size'] = isset($column_info['size']) && is_numeric($column_info['size']) ? $column_info['size'] : null;
440
		list ($type, $size) = $smcFunc['db_calculate_type']($column_info['type'], $column_info['size']);
441
		if ($size !== null)
442
			$type = $type . '(' . $size . ')';
443
444
		// The alter is a pain.
445
		$smcFunc['db_transaction']('begin');
446
		$smcFunc['db_query']('', '
447
			ALTER TABLE ' . $table_name . '
448
			ADD COLUMN ' . $column_info['name'] . '_tempxx ' . $type,
449
			array(
450
				'security_override' => true,
451
			)
452
		);
453
		$smcFunc['db_query']('', '
454
			UPDATE ' . $table_name . '
455
			SET ' . $column_info['name'] . '_tempxx = CAST(' . $column_info['name'] . ' AS ' . $type . ')',
456
			array(
457
				'security_override' => true,
458
			)
459
		);
460
		$smcFunc['db_query']('', '
461
			ALTER TABLE ' . $table_name . '
462
			DROP COLUMN ' . $column_info['name'],
463
			array(
464
				'security_override' => true,
465
			)
466
		);
467
		$smcFunc['db_query']('', '
468
			ALTER TABLE ' . $table_name . '
469
			RENAME COLUMN ' . $column_info['name'] . '_tempxx TO ' . $column_info['name'],
470
			array(
471
				'security_override' => true,
472
			)
473
		);
474
		$smcFunc['db_transaction']('commit');
475
	}
476
	// Finally - auto increment?!
477
	if (isset($column_info['auto']) && $column_info['auto'] != $old_info['auto'])
478
	{
479
		// Are we removing an old one?
480
		if ($old_info['auto'])
481
		{
482
			// Alter the table first - then drop the sequence.
483
			$smcFunc['db_query']('', '
484
				ALTER TABLE ' . $table_name . '
485
				ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT \'0\'',
486
				array(
487
					'security_override' => true,
488
				)
489
			);
490
			$smcFunc['db_query']('', '
491
				DROP SEQUENCE IF EXISTS ' . $table_name . '_seq',
492
				array(
493
					'security_override' => true,
494
				)
495
			);
496
		}
497
		// Otherwise add it!
498
		else
499
		{
500
			$smcFunc['db_query']('', '
501
				DROP SEQUENCE IF EXISTS ' . $table_name . '_seq',
502
				array(
503
					'security_override' => true,
504
				)
505
			);
506
507
			$smcFunc['db_query']('', '
508
				CREATE SEQUENCE ' . $table_name . '_seq',
509
				array(
510
					'security_override' => true,
511
				)
512
			);
513
			$smcFunc['db_query']('', '
514
				ALTER TABLE ' . $table_name . '
515
				ALTER COLUMN ' . $column_info['name'] . ' SET DEFAULT nextval(\'' . $table_name . '_seq\')',
516
				array(
517
					'security_override' => true,
518
				)
519
			);
520
		}
521
	}
522
523
	return true;
524
}
525
526
/**
527
 * Add an index.
528
 *
529
 * @param string $table_name The name of the table to add the index to
530
 * @param array $index_info An array of index info (see {@link smf_db_create_table()})
531
 * @param array $parameters Not used?
532
 * @param string $if_exists What to do if the index exists. If 'update', the definition will be updated.
533
 * @param string $error
534
 * @return boolean Whether or not the operation was successful
535
 */
536
function smf_db_add_index($table_name, $index_info, $parameters = array(), $if_exists = 'update', $error = 'fatal')
537
{
538
	global $smcFunc, $db_package_log, $db_prefix;
539
540
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
541
542
	// No columns = no index.
543
	if (empty($index_info['columns']))
544
		return false;
545
	$columns = implode(',', $index_info['columns']);
546
547
	// No name - make it up!
548
	if (empty($index_info['name']))
549
	{
550
		// No need for primary.
551
		if (isset($index_info['type']) && $index_info['type'] == 'primary')
552
			$index_info['name'] = '';
553
		else
554
			$index_info['name'] = $table_name . implode('_', $index_info['columns']);
555
	}
556
	else
557
		$index_info['name'] = $table_name . $index_info['name'];
558
559
	// Log that we are going to want to remove this!
560
	$db_package_log[] = array('remove_index', $table_name, $index_info['name']);
561
562
	// Let's get all our indexes.
563
	$indexes = $smcFunc['db_list_indexes']($table_name, true);
564
	// Do we already have it?
565
	foreach ($indexes as $index)
566
	{
567
		if ($index['name'] == $index_info['name'] || ($index['type'] == 'primary' && isset($index_info['type']) && $index_info['type'] == 'primary'))
568
		{
569
			// If we want to overwrite simply remove the current one then continue.
570
			if ($if_exists != 'update' || $index['type'] == 'primary')
571
				return false;
572
			else
573
				$smcFunc['db_remove_index']($table_name, $index_info['name']);
574
		}
575
	}
576
577
	// If we're here we know we don't have the index - so just add it.
578
	if (!empty($index_info['type']) && $index_info['type'] == 'primary')
579
	{
580
		$smcFunc['db_query']('', '
581
			ALTER TABLE ' . $table_name . '
582
			ADD PRIMARY KEY (' . $columns . ')',
583
			array(
584
				'security_override' => true,
585
			)
586
		);
587
	}
588
	else
589
	{
590
		$smcFunc['db_query']('', '
591
			CREATE ' . (isset($index_info['type']) && $index_info['type'] == 'unique' ? 'UNIQUE' : '') . ' INDEX ' . $index_info['name'] . ' ON ' . $table_name . ' (' . $columns . ')',
592
			array(
593
				'security_override' => true,
594
			)
595
		);
596
	}
597
}
598
599
/**
600
 * Remove an index.
601
 *
602
 * @param string $table_name The name of the table to remove the index from
603
 * @param string $index_name The name of the index to remove
604
 * @param array $parameters Not used?
605
 * @param string $error
606
 * @return boolean Whether or not the operation was successful
607
 */
608
function smf_db_remove_index($table_name, $index_name, $parameters = array(), $error = 'fatal')
609
{
610
	global $smcFunc, $db_prefix;
611
612
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
613
614
	// Better exist!
615
	$indexes = $smcFunc['db_list_indexes']($table_name, true);
616
	if ($index_name != 'primary')
617
		$index_name = $table_name . '_' . $index_name;
618
619 View Code Duplication
	foreach ($indexes as $index)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
620
	{
621
		// If the name is primary we want the primary key!
622
		if ($index['type'] == 'primary' && $index_name == 'primary')
623
		{
624
			// Dropping primary key is odd...
625
			$smcFunc['db_query']('', '
626
				ALTER TABLE ' . $table_name . '
627
				DROP CONSTRAINT ' . $index['name'],
628
				array(
629
					'security_override' => true,
630
				)
631
			);
632
633
			return true;
634
		}
635
		if ($index['name'] == $index_name)
636
		{
637
			// Drop the bugger...
638
			$smcFunc['db_query']('', '
639
				DROP INDEX ' . $index_name,
640
				array(
641
					'security_override' => true,
642
				)
643
			);
644
645
			return true;
646
		}
647
	}
648
649
	// Not to be found ;(
650
	return false;
651
}
652
653
/**
654
 * Get the schema formatted name for a type.
655
 *
656
 * @param string $type_name The data type (int, varchar, smallint, etc.)
657
 * @param int $type_size The size (8, 255, etc.)
658
 * @param boolean $reverse If true, returns specific types for a generic type
659
 * @return array An array containing the appropriate type and size for this DB type
660
 */
661
function smf_db_calculate_type($type_name, $type_size = null, $reverse = false)
662
{
663
	// Let's be sure it's lowercase MySQL likes both, others no.
664
	$type_name = strtolower($type_name);
665
	// Generic => Specific.
666
	if (!$reverse)
667
	{
668
		$types = array(
669
			'varchar' => 'character varying',
670
			'char' => 'character',
671
			'mediumint' => 'int',
672
			'tinyint' => 'smallint',
673
			'tinytext' => 'character varying',
674
			'mediumtext' => 'text',
675
			'largetext' => 'text',
676
			'inet' => 'inet',
677
			'time' => 'time without time zone',
678
			'datetime' => 'timestamp without time zone',
679
			'timestamp' => 'timestamp without time zone',
680
		);
681
	}
682
	else
683
	{
684
		$types = array(
685
			'character varying' => 'varchar',
686
			'character' => 'char',
687
			'integer' => 'int',
688
			'inet' => 'inet',
689
			'time without time zone' => 'time',
690
			'timestamp without time zone' => 'datetime',
691
			'numeric' => 'decimal',
692
		);
693
	}
694
695
	// Got it? Change it!
696
	if (isset($types[$type_name]))
697
	{
698
		if ($type_name == 'tinytext')
699
			$type_size = 255;
700
		$type_name = $types[$type_name];
701
	}
702
703
	// Only char fields got size
704
	if (strpos($type_name, 'char') === false)
705
			$type_size = null;
706
707
708
	return array($type_name, $type_size);
709
}
710
711
/**
712
 * Get table structure.
713
 *
714
 * @param string $table_name The name of the table
715
 * @return array An array of table structure - the name, the column info from {@link smf_db_list_columns()} and the index info from {@link smf_db_list_indexes()}
716
 */
717
function smf_db_table_structure($table_name)
718
{
719
	global $smcFunc, $db_prefix;
720
721
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
722
723
	return array(
724
		'name' => $table_name,
725
		'columns' => $smcFunc['db_list_columns']($table_name, true),
726
		'indexes' => $smcFunc['db_list_indexes']($table_name, true),
727
	);
728
}
729
730
/**
731
 * Return column information for a table.
732
 *
733
 * @param string $table_name The name of the table to get column info for
734
 * @param bool $detail Whether or not to return detailed info. If true, returns the column info. If false, just returns the column names.
735
 * @param array $parameters Not used?
736
 * @return array An array of column names or detailed column info, depending on $detail
737
 */
738
function smf_db_list_columns($table_name, $detail = false, $parameters = array())
739
{
740
	global $smcFunc, $db_prefix;
741
742
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
743
744
	$result = $smcFunc['db_query']('', '
745
		SELECT column_name, column_default, is_nullable, data_type, character_maximum_length
746
		FROM information_schema.columns
747
		WHERE table_schema = {string:schema_public}
748
		 AND table_name = {string:table_name}
749
		ORDER BY ordinal_position',
750
		array(
751
			'schema_public' => 'public',
752
			'table_name' => $table_name,
753
		)
754
	);
755
	$columns = array();
756
	while ($row = $smcFunc['db_fetch_assoc']($result))
757
	{
758
		if (!$detail)
759
		{
760
			$columns[] = $row['column_name'];
761
		}
762
		else
763
		{
764
			$auto = false;
765
			// What is the default?
766
			if (preg_match('~nextval\(\'(.+?)\'(.+?)*\)~i', $row['column_default'], $matches) != 0)
767
			{
768
				$default = null;
769
				$auto = true;
770
			}
771
			elseif (trim($row['column_default']) != '')
772
				$default = strpos($row['column_default'], '::') === false ? $row['column_default'] : substr($row['column_default'], 0, strpos($row['column_default'], '::'));
773
			else
774
				$default = null;
775
776
			// Make the type generic.
777
			list ($type, $size) = $smcFunc['db_calculate_type']($row['data_type'], $row['character_maximum_length'], true);
778
779
			$columns[$row['column_name']] = array(
780
				'name' => $row['column_name'],
781
				'null' => $row['is_nullable'] ? true : false,
782
				'default' => $default,
783
				'type' => $type,
784
				'size' => $size,
785
				'auto' => $auto,
786
			);
787
		}
788
	}
789
	$smcFunc['db_free_result']($result);
790
791
	return $columns;
792
}
793
794
/**
795
 * Get index information.
796
 *
797
 * @param string $table_name The name of the table to get indexes for
798
 * @param bool $detail Whether or not to return detailed info.
799
 * @param array $parameters Not used?
800
 * @return array An array of index names or a detailed array of index info, depending on $detail
801
 */
802
function smf_db_list_indexes($table_name, $detail = false, $parameters = array())
803
{
804
	global $smcFunc, $db_prefix;
805
806
	$table_name = str_replace('{db_prefix}', $db_prefix, $table_name);
807
808
	$result = $smcFunc['db_query']('', '
809
		SELECT CASE WHEN i.indisprimary THEN 1 ELSE 0 END AS is_primary,
810
			CASE WHEN i.indisunique THEN 1 ELSE 0 END AS is_unique,
811
			c2.relname AS name,
812
			pg_get_indexdef(i.indexrelid) AS inddef
813
		FROM pg_class AS c, pg_class AS c2, pg_index AS i
814
		WHERE c.relname = {string:table_name}
815
			AND c.oid = i.indrelid
816
			AND i.indexrelid = c2.oid',
817
		array(
818
			'table_name' => $table_name,
819
		)
820
	);
821
	$indexes = array();
822
	while ($row = $smcFunc['db_fetch_assoc']($result))
823
	{
824
		// Try get the columns that make it up.
825
		if (preg_match('~\(([^\)]+?)\)~i', $row['inddef'], $matches) == 0)
826
			continue;
827
828
		$columns = explode(',', $matches[1]);
829
830
		if (empty($columns))
831
			continue;
832
833
		foreach ($columns as $k => $v)
834
			$columns[$k] = trim($v);
835
836
		// Fix up the name to be consistent cross databases
837
		if (substr($row['name'], -5) == '_pkey' && $row['is_primary'] == 1)
838
			$row['name'] = 'PRIMARY';
839
		else
840
			$row['name'] = str_replace($table_name . '_', '', $row['name']);
841
842
		if (!$detail)
843
			$indexes[] = $row['name'];
844
		else
845
		{
846
			$indexes[$row['name']] = array(
847
				'name' => $row['name'],
848
				'type' => $row['is_primary'] ? 'primary' : ($row['is_unique'] ? 'unique' : 'index'),
849
				'columns' => $columns,
850
			);
851
		}
852
	}
853
	$smcFunc['db_free_result']($result);
854
855
	return $indexes;
856
}
857
858
?>