| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | <?php | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  | // This file is part of GNU social - https://www.gnu.org/software/social
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is distributed in the hope that it will be useful,
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |  * Database schema for PostgreSQL | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |  * @category  Database | 
					
						
							|  |  |  |  * @package   GNUsocial | 
					
						
							|  |  |  |  * @author    Evan Prodromou <evan@status.net> | 
					
						
							|  |  |  |  * @author    Brenda Wallace <shiny@cpan.org> | 
					
						
							|  |  |  |  * @author    Brion Vibber <brion@status.net> | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |  * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  | defined('GNUSOCIAL') || die(); | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |  * Class representing the database schema for PostgreSQL | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |  * | 
					
						
							|  |  |  |  * A class representing the database schema. Can be used to | 
					
						
							|  |  |  |  * manipulate the schema -- especially for plugins and upgrade | 
					
						
							|  |  |  |  * utilities. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |  * @copyright 2019 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |  */ | 
					
						
							|  |  |  | class PgsqlSchema extends Schema | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |     public static $_single = null; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Main public entry point. Use this to get | 
					
						
							|  |  |  |      * the singleton object. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param object|null $conn | 
					
						
							|  |  |  |      * @param string|null dummy param | 
					
						
							|  |  |  |      * @return Schema the (single) Schema object | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function get($conn = null, $_ = 'pgsql') | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (empty(self::$_single)) { | 
					
						
							|  |  |  |             self::$_single = new Schema($conn, 'pgsql'); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return self::$_single; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |      * Returns a table definition array for the table | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      * in the schema with the given name. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Throws an exception if the table is not found. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |      * @param string $table Name of the table to get | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |      * @return array tabledef for that table. | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @throws SchemaTableMissingException | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |     public function getTableDef($table) | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $def = []; | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         $hasKeys = false; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         // Pull column data from INFORMATION_SCHEMA
 | 
					
						
							|  |  |  |         $columns = $this->fetchMetaInfo($table, 'columns', 'ordinal_position'); | 
					
						
							|  |  |  |         if (count($columns) == 0) { | 
					
						
							|  |  |  |             throw new SchemaTableMissingException("No such table: $table"); | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |         // We'll need to match up fields by ordinal reference
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $orderedFields = []; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         foreach ($columns as $row) { | 
					
						
							|  |  |  |             $name = $row['column_name']; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |             $orderedFields[$row['ordinal_position']] = $name; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             $field = []; | 
					
						
							|  |  |  |             $field['type'] = $type = $row['udt_name']; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             if (in_array($type, ['char', 'bpchar', 'varchar'])) { | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |                 if ($row['character_maximum_length'] !== null) { | 
					
						
							|  |  |  |                     $field['length'] = intval($row['character_maximum_length']); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($type == 'numeric') { | 
					
						
							|  |  |  |                 // Other int types may report these values, but they're irrelevant.
 | 
					
						
							|  |  |  |                 // Just ignore them!
 | 
					
						
							|  |  |  |                 if ($row['numeric_precision'] !== null) { | 
					
						
							|  |  |  |                     $field['precision'] = intval($row['numeric_precision']); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if ($row['numeric_scale'] !== null) { | 
					
						
							|  |  |  |                     $field['scale'] = intval($row['numeric_scale']); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($row['is_nullable'] == 'NO') { | 
					
						
							|  |  |  |                 $field['not null'] = true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if ($row['column_default'] !== null) { | 
					
						
							|  |  |  |                 $field['default'] = $row['column_default']; | 
					
						
							| 
									
										
										
										
											2020-06-28 20:05:11 +03:00
										 |  |  |                 if ($this->isNumericType($field)) { | 
					
						
							|  |  |  |                     $field['default'] = (int) $field['default']; | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |             $def['fields'][$name] = $field; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |         // Pulling index info from pg_class & pg_index
 | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         // This can give us primary & unique key info, but not foreign key constraints
 | 
					
						
							|  |  |  |         // so we exclude them and pick them up later.
 | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |         $indexInfo = $this->fetchIndexInfo($table); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |         foreach ($indexInfo as $row) { | 
					
						
							|  |  |  |             $keyName = $row['key_name']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             // Dig the column references out!
 | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |             //
 | 
					
						
							|  |  |  |             // These are inconvenient arrays with partial references to the
 | 
					
						
							|  |  |  |             // pg_att table, but since we've already fetched up the column
 | 
					
						
							|  |  |  |             // info on the current table, we can look those up locally.
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             $cols = []; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |             $colPositions = explode(' ', $row['indkey']); | 
					
						
							|  |  |  |             foreach ($colPositions as $ord) { | 
					
						
							|  |  |  |                 if ($ord == 0) { | 
					
						
							|  |  |  |                     $cols[] = 'FUNCTION'; // @fixme
 | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     $cols[] = $orderedFields[$ord]; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |             $def['indexes'][$keyName] = $cols; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         // Pull constraint data from INFORMATION_SCHEMA:
 | 
					
						
							|  |  |  |         // Primary key, unique keys, foreign keys
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         $keyColumns = $this->fetchMetaInfo($table, 'key_column_usage', 'constraint_name,ordinal_position'); | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $keys = []; | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         foreach ($keyColumns as $row) { | 
					
						
							|  |  |  |             $keyName = $row['constraint_name']; | 
					
						
							|  |  |  |             $keyCol = $row['column_name']; | 
					
						
							|  |  |  |             if (!isset($keys[$keyName])) { | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |                 $keys[$keyName] = []; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |             $keys[$keyName][] = $keyCol; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         foreach ($keys as $keyName => $cols) { | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |             // name hack -- is this reliable?
 | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |             if ($keyName == "{$table}_pkey") { | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |                 $def['primary key'] = $cols; | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |             } elseif (preg_match("/^{$table}_(.*)_fkey$/", $keyName, $matches)) { | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |                 $fkey = $this->fetchForeignKeyInfo($table, $keyName); | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |                 $colMap = array_combine($cols, $fkey['col_names']); | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |                 $def['foreign keys'][$keyName] = [$fkey['table_name'], $colMap]; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |                 $def['unique keys'][$keyName] = $cols; | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         } | 
					
						
							|  |  |  |         return $def; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Pull some INFORMATION.SCHEMA data for the given table. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $table | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @param $infoTable | 
					
						
							|  |  |  |      * @param null $orderBy | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |      * @return array of arrays | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @throws PEAR_Exception | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |     public function fetchMetaInfo($table, $infoTable, $orderBy = null) | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         $query = "SELECT * FROM information_schema.%s " . | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             "WHERE table_name='%s'"; | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         $sql = sprintf($query, $infoTable, $table); | 
					
						
							|  |  |  |         if ($orderBy) { | 
					
						
							|  |  |  |             $sql .= ' ORDER BY ' . $orderBy; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-10-08 16:36:32 -07:00
										 |  |  |         return $this->fetchQueryData($sql); | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |      * Pull some PG-specific index info | 
					
						
							|  |  |  |      * @param string $table | 
					
						
							|  |  |  |      * @return array of arrays | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @throws PEAR_Exception | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |     public function fetchIndexInfo(string $table): array | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-07-21 18:06:39 +03:00
										 |  |  |         return $this->fetchQueryData( | 
					
						
							|  |  |  |             <<<END | 
					
						
							|  |  |  |             SELECT indexname AS key_name, indexdef AS key_def, pg_index.* | 
					
						
							|  |  |  |               FROM pg_index INNER JOIN pg_indexes | 
					
						
							|  |  |  |               ON pg_index.indexrelid = CAST(pg_indexes.indexname AS regclass) | 
					
						
							|  |  |  |               WHERE pg_indexes.tablename = '{$table}' | 
					
						
							|  |  |  |               AND indisprimary IS FALSE AND indisunique IS FALSE | 
					
						
							|  |  |  |               ORDER BY indrelid, indexrelid; | 
					
						
							|  |  |  |             END | 
					
						
							|  |  |  |         ); | 
					
						
							| 
									
										
										
										
											2010-10-11 14:15:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @param string $table | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |      * @param string $constraint_name | 
					
						
							|  |  |  |      * @return array array of rows with keys: table_name, col_names (array of strings) | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @throws PEAR_Exception | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |     public function fetchForeignKeyInfo(string $table, string $constraint_name): array | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         // In a sane world, it'd be easier to query the column names directly.
 | 
					
						
							|  |  |  |         // But it's pretty hard to work with arrays such as col_indexes in direct SQL here.
 | 
					
						
							|  |  |  |         $query = 'SELECT ' . | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             '(SELECT relname FROM pg_class WHERE oid = confrelid) AS table_name, ' . | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             'confrelid AS table_id, ' . | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             '(SELECT indkey FROM pg_index WHERE indexrelid = conindid) AS col_indices ' . | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             'FROM pg_constraint ' . | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             'WHERE conrelid = CAST(\'%s\' AS regclass) AND conname = \'%s\' AND contype = \'f\''; | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         $sql = sprintf($query, $table, $constraint_name); | 
					
						
							|  |  |  |         $data = $this->fetchQueryData($sql); | 
					
						
							|  |  |  |         if (count($data) < 1) { | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             throw new Exception('Could not find foreign key ' . $constraint_name . ' on table ' . $table); | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $row = $data[0]; | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         return [ | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |             'table_name' => $row['table_name'], | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             'col_names' => $this->getTableColumnNames($row['table_id'], $row['col_indices']) | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param int $table_id | 
					
						
							|  |  |  |      * @param array $col_indexes | 
					
						
							|  |  |  |      * @return array of strings | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @throws PEAR_Exception | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |     public function getTableColumnNames($table_id, $col_indexes) | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         $indexes = array_map('intval', explode(' ', $col_indexes)); | 
					
						
							|  |  |  |         $query = 'SELECT attnum AS col_index, attname AS col_name ' . | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             'FROM pg_attribute where attrelid=%d ' . | 
					
						
							|  |  |  |             'AND attnum IN (%s)'; | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         $sql = sprintf($query, $table_id, implode(',', $indexes)); | 
					
						
							|  |  |  |         $data = $this->fetchQueryData($sql); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $byId = []; | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         foreach ($data as $row) { | 
					
						
							|  |  |  |             $byId[$row['col_index']] = $row['col_name']; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $out = []; | 
					
						
							| 
									
										
										
										
											2010-10-11 15:40:51 -07:00
										 |  |  |         foreach ($indexes as $id) { | 
					
						
							|  |  |  |             $out[] = $byId[$id]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $out; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-28 20:05:11 +03:00
										 |  |  |     private function isNumericType(array $cd): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $ints = ['int', 'numeric', 'serial']; | 
					
						
							|  |  |  |         return in_array(strtolower($cd['type']), $ints); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |      * Return the proper SQL for creating or | 
					
						
							|  |  |  |      * altering a column. | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |      * Appropriate for use in CREATE TABLE or | 
					
						
							|  |  |  |      * ALTER TABLE statements. | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  |      * @param string $name column name to create | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |      * @param array $cd column to create | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |      * @return string correct SQL for that column | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  |     public function columnSql(string $name, array $cd) | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $line = []; | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  |         $line[] = parent::columnSql($name, $cd); | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-11 19:10:51 -07:00
										 |  |  |         /* | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |         if ($table['foreign keys'][$name]) { | 
					
						
							|  |  |  |             foreach ($table['foreign keys'][$name] as $foreignTable => $foreignColumn) { | 
					
						
							|  |  |  |                 $line[] = 'references'; | 
					
						
							| 
									
										
										
										
											2010-10-11 19:10:51 -07:00
										 |  |  |                 $line[] = $this->quoteIdentifier($foreignTable); | 
					
						
							|  |  |  |                 $line[] = '(' . $this->quoteIdentifier($foreignColumn) . ')'; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-10-11 19:10:51 -07:00
										 |  |  |         */ | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |         // This'll have been added from our transform of 'serial' type
 | 
					
						
							|  |  |  |         if (!empty($cd['auto_increment'])) { | 
					
						
							|  |  |  |             $line[] = 'GENERATED BY DEFAULT AS IDENTITY'; | 
					
						
							|  |  |  |         } elseif (!empty($cd['enum'])) { | 
					
						
							|  |  |  |             foreach ($cd['enum'] as &$val) { | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  |                 $vals[] = "'" . $val . "'"; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $line[] = 'CHECK (' . $name . ' IN (' . implode(',', $vals) . '))'; | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |         return implode(' ', $line); | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |     public function appendAlterDropPrimary(array &$phrase, string $tableName) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // name hack -- is this reliable?
 | 
					
						
							|  |  |  |         $phrase[] = 'DROP CONSTRAINT ' . $this->quoteIdentifier($tableName . '_pkey'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |     public function mapType($column) | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         $map = [ | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |             'integer'  => 'int', | 
					
						
							|  |  |  |             'char'     => 'bpchar', | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |             'datetime' => 'timestamp', | 
					
						
							| 
									
										
										
										
											2019-09-11 11:25:39 +03:00
										 |  |  |             'blob'     => 'bytea', | 
					
						
							| 
									
										
										
										
											2019-09-11 12:48:28 +03:00
										 |  |  |             'enum'     => 'text', | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $type = $column['type']; | 
					
						
							|  |  |  |         if (isset($map[$type])) { | 
					
						
							|  |  |  |             $type = $map[$type]; | 
					
						
							| 
									
										
										
										
											2010-05-15 14:56:40 +12:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |         $size = $column['size'] ?? null; | 
					
						
							|  |  |  |         if ($type === 'int') { | 
					
						
							|  |  |  |             if (in_array($size, ['tiny', 'small'])) { | 
					
						
							|  |  |  |                 $type = 'int2'; | 
					
						
							|  |  |  |             } elseif ($size === 'big') { | 
					
						
							|  |  |  |                 $type = 'int8'; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 $type = 'int4'; | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  |         } elseif ($type === 'float') { | 
					
						
							|  |  |  |             $type = ($size !== 'big') ? 'float4' : 'float8'; | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-08-16 16:31:18 -07:00
										 |  |  |         return $type; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Filter the given table definition array to match features available | 
					
						
							|  |  |  |      * in this database. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * This lets us strip out unsupported things like comments, foreign keys, | 
					
						
							|  |  |  |      * or type variants that we wouldn't get back from getTableDef(). | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2020-06-28 20:05:11 +03:00
										 |  |  |      * @param string $tableName | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |      * @param array $tableDef | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |      * @return array | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-06-28 20:05:11 +03:00
										 |  |  |     public function filterDef(string $tableName, array $tableDef) | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-06-28 20:05:11 +03:00
										 |  |  |         $tableDef = parent::filterDef($tableName, $tableDef); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-13 16:21:24 -07:00
										 |  |  |         foreach ($tableDef['fields'] as $name => &$col) { | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |             // No convenient support for field descriptions
 | 
					
						
							|  |  |  |             unset($col['description']); | 
					
						
							| 
									
										
										
										
											2020-07-31 18:24:12 +03:00
										 |  |  |             // @fixme Nor for MariaDB-specific collations
 | 
					
						
							|  |  |  |             unset($col['collate']); | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-29 01:41:46 +03:00
										 |  |  |             if ($col['type'] === 'serial') { | 
					
						
							|  |  |  |                 $col['type'] = 'int'; | 
					
						
							|  |  |  |                 $col['auto_increment'] = true; | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-15 16:32:37 -07:00
										 |  |  |             $col['type'] = $this->mapType($col); | 
					
						
							|  |  |  |             unset($col['size']); | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-09-11 14:14:40 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-18 18:26:11 -07:00
										 |  |  |         if (!empty($tableDef['primary key'])) { | 
					
						
							|  |  |  |             $tableDef['primary key'] = $this->filterKeyDef($tableDef['primary key']); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!empty($tableDef['unique keys'])) { | 
					
						
							|  |  |  |             foreach ($tableDef['unique keys'] as $i => $def) { | 
					
						
							|  |  |  |                 $tableDef['unique keys'][$i] = $this->filterKeyDef($def); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-10-13 16:04:28 -07:00
										 |  |  |         return $tableDef; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-10-18 18:26:11 -07:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Filter the given key/index definition to match features available | 
					
						
							|  |  |  |      * in this database. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param array $def | 
					
						
							|  |  |  |      * @return array | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-09-11 08:15:16 +03:00
										 |  |  |     public function filterKeyDef(array $def) | 
					
						
							| 
									
										
										
										
											2010-10-18 18:26:11 -07:00
										 |  |  |     { | 
					
						
							|  |  |  |         // PostgreSQL doesn't like prefix lengths specified on keys...?
 | 
					
						
							| 
									
										
										
										
											2019-07-25 00:43:25 +01:00
										 |  |  |         foreach ($def as $i => $item) { | 
					
						
							| 
									
										
										
										
											2010-10-18 18:26:11 -07:00
										 |  |  |             if (is_array($item)) { | 
					
						
							|  |  |  |                 $def[$i] = $item[0]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $def; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2010-01-30 18:45:10 +13:00
										 |  |  | } |