Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
postgres.php
001 <?php
002 /**
003 *
004 * This file is part of the phpBB Forum Software package.
005 *
006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
007 * @license GNU General Public License, version 2 (GPL-2.0)
008 *
009 * For full copyright and license information, please see
010 * the docs/CREDITS.txt file.
011 *
012 */
013
014 namespace phpbb\db\tools;
015
016 /**
017 * Database Tools for handling cross-db actions such as altering columns, etc.
018 * Currently not supported is returning SQL for creating tables.
019 */
020 class postgres extends tools
021 {
022 /**
023 * Get the column types for postgres only
024 *
025 * @return array
026 */
027 public static function get_dbms_type_map()
028 {
029 return array(
030 'postgres' => array(
031 'INT:' => 'INT4',
032 'BINT' => 'INT8',
033 'ULINT' => 'INT4', // unsigned
034 'UINT' => 'INT4', // unsigned
035 'UINT:' => 'INT4', // unsigned
036 'USINT' => 'INT2', // unsigned
037 'BOOL' => 'INT2', // unsigned
038 'TINT:' => 'INT2',
039 'VCHAR' => 'varchar(255)',
040 'VCHAR:' => 'varchar(%d)',
041 'CHAR:' => 'char(%d)',
042 'XSTEXT' => 'varchar(1000)',
043 'STEXT' => 'varchar(3000)',
044 'TEXT' => 'varchar(8000)',
045 'MTEXT' => 'TEXT',
046 'XSTEXT_UNI'=> 'varchar(100)',
047 'STEXT_UNI' => 'varchar(255)',
048 'TEXT_UNI' => 'varchar(4000)',
049 'MTEXT_UNI' => 'TEXT',
050 'TIMESTAMP' => 'INT4', // unsigned
051 'DECIMAL' => 'decimal(5,2)',
052 'DECIMAL:' => 'decimal(%d,2)',
053 'PDECIMAL' => 'decimal(6,3)',
054 'PDECIMAL:' => 'decimal(%d,3)',
055 'VCHAR_UNI' => 'varchar(255)',
056 'VCHAR_UNI:'=> 'varchar(%d)',
057 'VCHAR_CI' => 'varchar_ci',
058 'VARBINARY' => 'bytea',
059 ),
060 );
061 }
062
063 /**
064 * Constructor. Set DB Object and set {@link $return_statements return_statements}.
065 *
066 * @param \phpbb\db\driver\driver_interface $db Database connection
067 * @param bool $return_statements True if only statements should be returned and no SQL being executed
068 */
069 public function __construct(\phpbb\db\driver\driver_interface $db, $return_statements = false)
070 {
071 parent::__construct($db, $return_statements);
072
073 // Determine mapping database type
074 $this->sql_layer = 'postgres';
075
076 $this->dbms_type_map = self::get_dbms_type_map();
077 }
078
079 /**
080 * {@inheritDoc}
081 */
082 function sql_list_tables()
083 {
084 $sql = 'SELECT relname
085 FROM pg_stat_user_tables';
086 $result = $this->db->sql_query($sql);
087
088 $tables = array();
089 while ($row = $this->db->sql_fetchrow($result))
090 {
091 $name = current($row);
092 $tables[$name] = $name;
093 }
094 $this->db->sql_freeresult($result);
095
096 return $tables;
097 }
098
099 /**
100 * {@inheritDoc}
101 */
102 function sql_create_table($table_name, $table_data)
103 {
104 // holds the DDL for a column
105 $columns = $statements = array();
106
107 if ($this->sql_table_exists($table_name))
108 {
109 return $this->_sql_run_sql($statements);
110 }
111
112 // Begin transaction
113 $statements[] = 'begin';
114
115 // Determine if we have created a PRIMARY KEY in the earliest
116 $primary_key_gen = false;
117
118 // Determine if the table requires a sequence
119 $create_sequence = false;
120
121 // Begin table sql statement
122 $table_sql = 'CREATE TABLE ' . $table_name . ' (' . "\n";
123
124 // Iterate through the columns to create a table
125 foreach ($table_data['COLUMNS'] as $column_name => $column_data)
126 {
127 // here lies an array, filled with information compiled on the column's data
128 $prepared_column = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
129
130 if (isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'] && strlen($column_name) > 26) // "${column_name}_gen"
131 {
132 trigger_error("Index name '${column_name}_gen' on table '$table_name' is too long. The maximum auto increment column length is 26 characters.", E_USER_ERROR);
133 }
134
135 // here we add the definition of the new column to the list of columns
136 $columns[] = "\t {$column_name} " . $prepared_column['column_type_sql'];
137
138 // see if we have found a primary key set due to a column definition if we have found it, we can stop looking
139 if (!$primary_key_gen)
140 {
141 $primary_key_gen = isset($prepared_column['primary_key_set']) && $prepared_column['primary_key_set'];
142 }
143
144 // create sequence DDL based off of the existance of auto incrementing columns
145 if (!$create_sequence && isset($prepared_column['auto_increment']) && $prepared_column['auto_increment'])
146 {
147 $create_sequence = $column_name;
148 }
149 }
150
151 // this makes up all the columns in the create table statement
152 $table_sql .= implode(",\n", $columns);
153
154 // we have yet to create a primary key for this table,
155 // this means that we can add the one we really wanted instead
156 if (!$primary_key_gen)
157 {
158 // Write primary key
159 if (isset($table_data['PRIMARY_KEY']))
160 {
161 if (!is_array($table_data['PRIMARY_KEY']))
162 {
163 $table_data['PRIMARY_KEY'] = array($table_data['PRIMARY_KEY']);
164 }
165
166 $table_sql .= ",\n\t PRIMARY KEY (" . implode(', ', $table_data['PRIMARY_KEY']) . ')';
167 }
168 }
169
170 // do we need to add a sequence for auto incrementing columns?
171 if ($create_sequence)
172 {
173 $statements[] = "CREATE SEQUENCE {$table_name}_seq;";
174 }
175
176 // close the table
177 $table_sql .= "\n);";
178 $statements[] = $table_sql;
179
180 // Write Keys
181 if (isset($table_data['KEYS']))
182 {
183 foreach ($table_data['KEYS'] as $key_name => $key_data)
184 {
185 if (!is_array($key_data[1]))
186 {
187 $key_data[1] = array($key_data[1]);
188 }
189
190 $old_return_statements = $this->return_statements;
191 $this->return_statements = true;
192
193 $key_stmts = ($key_data[0] == 'UNIQUE') ? $this->sql_create_unique_index($table_name, $key_name, $key_data[1]) : $this->sql_create_index($table_name, $key_name, $key_data[1]);
194
195 foreach ($key_stmts as $key_stmt)
196 {
197 $statements[] = $key_stmt;
198 }
199
200 $this->return_statements = $old_return_statements;
201 }
202 }
203
204 // Commit Transaction
205 $statements[] = 'commit';
206
207 return $this->_sql_run_sql($statements);
208 }
209
210 /**
211 * {@inheritDoc}
212 */
213 function sql_list_columns($table_name)
214 {
215 $columns = array();
216
217 $sql = "SELECT a.attname
218 FROM pg_class c, pg_attribute a
219 WHERE c.relname = '{$table_name}'
220 AND a.attnum > 0
221 AND a.attrelid = c.oid";
222 $result = $this->db->sql_query($sql);
223
224 while ($row = $this->db->sql_fetchrow($result))
225 {
226 $column = strtolower(current($row));
227 $columns[$column] = $column;
228 }
229 $this->db->sql_freeresult($result);
230
231 return $columns;
232 }
233
234 /**
235 * {@inheritDoc}
236 */
237 function sql_index_exists($table_name, $index_name)
238 {
239 $sql = "SELECT ic.relname as index_name
240 FROM pg_class bc, pg_class ic, pg_index i
241 WHERE (bc.oid = i.indrelid)
242 AND (ic.oid = i.indexrelid)
243 AND (bc.relname = '" . $table_name . "')
244 AND (i.indisunique != 't')
245 AND (i.indisprimary != 't')";
246 $result = $this->db->sql_query($sql);
247
248 while ($row = $this->db->sql_fetchrow($result))
249 {
250 // This DBMS prefixes index names with the table name
251 $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
252
253 if (strtolower($row['index_name']) == strtolower($index_name))
254 {
255 $this->db->sql_freeresult($result);
256 return true;
257 }
258 }
259 $this->db->sql_freeresult($result);
260
261 return false;
262 }
263
264 /**
265 * {@inheritDoc}
266 */
267 function sql_unique_index_exists($table_name, $index_name)
268 {
269 $sql = "SELECT ic.relname as index_name, i.indisunique
270 FROM pg_class bc, pg_class ic, pg_index i
271 WHERE (bc.oid = i.indrelid)
272 AND (ic.oid = i.indexrelid)
273 AND (bc.relname = '" . $table_name . "')
274 AND (i.indisprimary != 't')";
275 $result = $this->db->sql_query($sql);
276
277 while ($row = $this->db->sql_fetchrow($result))
278 {
279 if ($row['indisunique'] != 't')
280 {
281 continue;
282 }
283
284 // This DBMS prefixes index names with the table name
285 $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
286
287 if (strtolower($row['index_name']) == strtolower($index_name))
288 {
289 $this->db->sql_freeresult($result);
290 return true;
291 }
292 }
293 $this->db->sql_freeresult($result);
294
295 return false;
296 }
297
298 /**
299 * Function to prepare some column information for better usage
300 * @access private
301 */
302 function sql_prepare_column_data($table_name, $column_name, $column_data)
303 {
304 if (strlen($column_name) > 30)
305 {
306 trigger_error("Column name '$column_name' on table '$table_name' is too long. The maximum is 30 characters.", E_USER_ERROR);
307 }
308
309 // Get type
310 list($column_type, $orig_column_type) = $this->get_column_type($column_data[0]);
311
312 // Adjust default value if db-dependent specified
313 if (is_array($column_data[1]))
314 {
315 $column_data[1] = (isset($column_data[1][$this->sql_layer])) ? $column_data[1][$this->sql_layer] : $column_data[1]['default'];
316 }
317
318 $sql = " {$column_type} ";
319
320 $return_array = array(
321 'column_type' => $column_type,
322 'auto_increment' => false,
323 );
324
325 if (isset($column_data[2]) && $column_data[2] == 'auto_increment')
326 {
327 $default_val = "nextval('{$table_name}_seq')";
328 $return_array['auto_increment'] = true;
329 }
330 else if (!is_null($column_data[1]))
331 {
332 $default_val = "'" . $column_data[1] . "'";
333 $return_array['null'] = 'NOT NULL';
334 $sql .= 'NOT NULL ';
335 }
336 else
337 {
338 // Integers need to have 0 instead of empty string as default
339 if (strpos($column_type, 'INT') === 0)
340 {
341 $default_val = '0';
342 }
343 else
344 {
345 $default_val = "'" . $column_data[1] . "'";
346 }
347 $return_array['null'] = 'NULL';
348 $sql .= 'NULL ';
349 }
350
351 $return_array['default'] = $default_val;
352
353 $sql .= "DEFAULT {$default_val}";
354
355 // Unsigned? Then add a CHECK contraint
356 if (in_array($orig_column_type, $this->unsigned_types))
357 {
358 $return_array['constraint'] = "CHECK ({$column_name} >= 0)";
359 $sql .= " CHECK ({$column_name} >= 0)";
360 }
361
362 $return_array['column_type_sql'] = $sql;
363
364 return $return_array;
365 }
366
367 /**
368 * {@inheritDoc}
369 */
370 function sql_column_add($table_name, $column_name, $column_data, $inline = false)
371 {
372 $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
373 $statements = array();
374
375 // Does not support AFTER, only through temporary table
376 if (version_compare($this->db->sql_server_info(true), '8.0', '>='))
377 {
378 $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type_sql'];
379 }
380 else
381 {
382 // old versions cannot add columns with default and null information
383 $statements[] = 'ALTER TABLE ' . $table_name . ' ADD COLUMN "' . $column_name . '" ' . $column_data['column_type'] . ' ' . $column_data['constraint'];
384
385 if (isset($column_data['null']))
386 {
387 if ($column_data['null'] == 'NOT NULL')
388 {
389 $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET NOT NULL';
390 }
391 }
392
393 if (isset($column_data['default']))
394 {
395 $statements[] = 'ALTER TABLE ' . $table_name . ' ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
396 }
397 }
398
399 return $this->_sql_run_sql($statements);
400 }
401
402 /**
403 * {@inheritDoc}
404 */
405 function sql_column_remove($table_name, $column_name, $inline = false)
406 {
407 $statements = array();
408
409 $statements[] = 'ALTER TABLE ' . $table_name . ' DROP COLUMN "' . $column_name . '"';
410
411 return $this->_sql_run_sql($statements);
412 }
413
414 /**
415 * {@inheritDoc}
416 */
417 function sql_index_drop($table_name, $index_name)
418 {
419 $statements = array();
420
421 $statements[] = 'DROP INDEX ' . $table_name . '_' . $index_name;
422
423 return $this->_sql_run_sql($statements);
424 }
425
426 /**
427 * {@inheritDoc}
428 */
429 function sql_table_drop($table_name)
430 {
431 $statements = array();
432
433 if (!$this->sql_table_exists($table_name))
434 {
435 return $this->_sql_run_sql($statements);
436 }
437
438 // the most basic operation, get rid of the table
439 $statements[] = 'DROP TABLE ' . $table_name;
440
441 // PGSQL does not "tightly" bind sequences and tables, we must guess...
442 $sql = "SELECT relname
443 FROM pg_class
444 WHERE relkind = 'S'
445 AND relname = '{$table_name}_seq'";
446 $result = $this->db->sql_query($sql);
447
448 // We don't even care about storing the results. We already know the answer if we get rows back.
449 if ($this->db->sql_fetchrow($result))
450 {
451 $statements[] = "DROP SEQUENCE {$table_name}_seq;\n";
452 }
453 $this->db->sql_freeresult($result);
454
455 return $this->_sql_run_sql($statements);
456 }
457
458 /**
459 * {@inheritDoc}
460 */
461 function sql_create_primary_key($table_name, $column, $inline = false)
462 {
463 $statements = array();
464
465 $statements[] = 'ALTER TABLE ' . $table_name . ' ADD PRIMARY KEY (' . implode(', ', $column) . ')';
466
467 return $this->_sql_run_sql($statements);
468 }
469
470 /**
471 * {@inheritDoc}
472 */
473 function sql_create_unique_index($table_name, $index_name, $column)
474 {
475 $statements = array();
476
477 $this->check_index_name_length($table_name, $index_name);
478
479 $statements[] = 'CREATE UNIQUE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
480
481 return $this->_sql_run_sql($statements);
482 }
483
484 /**
485 * {@inheritDoc}
486 */
487 function sql_create_index($table_name, $index_name, $column)
488 {
489 $statements = array();
490
491 $this->check_index_name_length($table_name, $index_name);
492
493 // remove index length
494 $column = preg_replace('#:.*$#', '', $column);
495
496 $statements[] = 'CREATE INDEX ' . $table_name . '_' . $index_name . ' ON ' . $table_name . '(' . implode(', ', $column) . ')';
497
498 return $this->_sql_run_sql($statements);
499 }
500
501
502 /**
503 * {@inheritDoc}
504 */
505 function sql_list_index($table_name)
506 {
507 $index_array = array();
508
509 $sql = "SELECT ic.relname as index_name
510 FROM pg_class bc, pg_class ic, pg_index i
511 WHERE (bc.oid = i.indrelid)
512 AND (ic.oid = i.indexrelid)
513 AND (bc.relname = '" . $table_name . "')
514 AND (i.indisunique != 't')
515 AND (i.indisprimary != 't')";
516 $result = $this->db->sql_query($sql);
517
518 while ($row = $this->db->sql_fetchrow($result))
519 {
520 $row['index_name'] = $this->strip_table_name_from_index_name($table_name, $row['index_name']);
521
522 $index_array[] = $row['index_name'];
523 }
524 $this->db->sql_freeresult($result);
525
526 return array_map('strtolower', $index_array);
527 }
528
529 /**
530 * {@inheritDoc}
531 */
532 function sql_column_change($table_name, $column_name, $column_data, $inline = false)
533 {
534 $column_data = $this->sql_prepare_column_data($table_name, $column_name, $column_data);
535 $statements = array();
536
537 $sql = 'ALTER TABLE ' . $table_name . ' ';
538
539 $sql_array = array();
540 $sql_array[] = 'ALTER COLUMN ' . $column_name . ' TYPE ' . $column_data['column_type'];
541
542 if (isset($column_data['null']))
543 {
544 if ($column_data['null'] == 'NOT NULL')
545 {
546 $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET NOT NULL';
547 }
548 else if ($column_data['null'] == 'NULL')
549 {
550 $sql_array[] = 'ALTER COLUMN ' . $column_name . ' DROP NOT NULL';
551 }
552 }
553
554 if (isset($column_data['default']))
555 {
556 $sql_array[] = 'ALTER COLUMN ' . $column_name . ' SET DEFAULT ' . $column_data['default'];
557 }
558
559 // we don't want to double up on constraints if we change different number data types
560 if (isset($column_data['constraint']))
561 {
562 $constraint_sql = "SELECT consrc as constraint_data
563 FROM pg_constraint, pg_class bc
564 WHERE conrelid = bc.oid
565 AND bc.relname = '{$table_name}'
566 AND NOT EXISTS (
567 SELECT *
568 FROM pg_constraint as c, pg_inherits as i
569 WHERE i.inhrelid = pg_constraint.conrelid
570 AND c.conname = pg_constraint.conname
571 AND c.consrc = pg_constraint.consrc
572 AND c.conrelid = i.inhparent
573 )";
574
575 $constraint_exists = false;
576
577 $result = $this->db->sql_query($constraint_sql);
578 while ($row = $this->db->sql_fetchrow($result))
579 {
580 if (trim($row['constraint_data']) == trim($column_data['constraint']))
581 {
582 $constraint_exists = true;
583 break;
584 }
585 }
586 $this->db->sql_freeresult($result);
587
588 if (!$constraint_exists)
589 {
590 $sql_array[] = 'ADD ' . $column_data['constraint'];
591 }
592 }
593
594 $sql .= implode(', ', $sql_array);
595
596 $statements[] = $sql;
597
598 return $this->_sql_run_sql($statements);
599 }
600
601 /**
602 * Get a list with existing indexes for the column
603 *
604 * @param string $table_name
605 * @param string $column_name
606 * @param bool $unique Should we get unique indexes or normal ones
607 * @return array Array with Index name => columns
608 */
609 public function get_existing_indexes($table_name, $column_name, $unique = false)
610 {
611 // Not supported
612 throw new \Exception('DBMS is not supported');
613 }
614 }
615