X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Fpgsqlschema.php;h=d50e35f660f65587c89809be2f85198d0c80b04f;hb=b53e1439969bfa2c0b551d8cc2fc8fe15652c62a;hp=4e24da7501b68f9500eb95f80ef3d1e20e331841;hpb=5434f431769faa4378be61eec6e736ee3e1dba0b;p=quix0rs-gnu-social.git diff --git a/lib/pgsqlschema.php b/lib/pgsqlschema.php index 4e24da7501..d50e35f660 100644 --- a/lib/pgsqlschema.php +++ b/lib/pgsqlschema.php @@ -81,13 +81,7 @@ class PgsqlSchema extends Schema $orderedFields[$row['ordinal_position']] = $name; $field = array(); - - // ?? - list($type, $size) = $this->reverseMapType($row['udt_name']); - $field['type'] = $type; - if ($size !== null) { - $field['size'] = $size; - } + $field['type'] = $row['udt_name']; if ($type == 'char' || $type == 'varchar') { if ($row['character_maximum_length'] !== null) { @@ -118,18 +112,20 @@ class PgsqlSchema extends Schema } // Pulling index info from pg_class & pg_index - // This can provide us basic info on primary, unique, and multi-val keys - // But... it doesn't list plain constraints or foreign key constraints. :P + // This can give us primary & unique key info, but not foreign key constraints + // so we exclude them and pick them up later. $indexInfo = $this->getIndexInfo($table); foreach ($indexInfo as $row) { $keyName = $row['key_name']; // Dig the column references out! + // + // 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. $cols = array(); $colPositions = explode(' ', $row['indkey']); foreach ($colPositions as $ord) { - // ordinal_position from above is 1-based - // but values in indkey are 0-based if ($ord == 0) { $cols[] = 'FUNCTION'; // @fixme } else { @@ -137,27 +133,15 @@ class PgsqlSchema extends Schema } } - // @fixme foreign keys? - // @fixme prefixes? - // @fixme funky stuff like fulltext? - if ($row['indisprimary'] == 't') { - $def['primary key'] = $cols; - } else if ($row['indisunique'] == 't') { - $def['unique keys'][$keyName] = $cols; - } else { - $def['indexes'][$keyName] = $cols; - } + $def['indexes'][$keyName] = $cols; } - // Pull constraint data from INFORMATION_SCHEMA - // @fixme also find multi-val indexes - // @fixme distinguish the primary key - // @fixme pull foreign key references + // Pull constraint data from INFORMATION_SCHEMA: + // Primary key, unique keys, foreign keys $keyColumns = $this->fetchMetaInfo($table, 'key_column_usage', 'constraint_name,ordinal_position'); $keys = array(); foreach ($keyColumns as $row) { - var_dump($row); $keyName = $row['constraint_name']; $keyCol = $row['column_name']; if (!isset($keys[$keyName])) { @@ -167,17 +151,15 @@ class PgsqlSchema extends Schema } foreach ($keys as $keyName => $cols) { - // hack -- is this reliable? + // name hack -- is this reliable? if ($keyName == "{$table}_pkey") { - $def['xprimary key'] = $cols; + $def['primary key'] = $cols; } else if (preg_match("/^{$table}_(.*)_fkey$/", $keyName, $matches)) { - $keys = array_keys($cols); - if (count($cols) == 1 && $cols[$keys[0]] == $keyName) { - $def['foreign keys'][$keyname][$matches[1]] = $keys[0]; - } - $def['foreign keys'][$keyName][$matches[1]] = $cols; + $fkey = $this->getForeignKeyInfo($table, $keyName); + $colMap = array_combine($cols, $fkey['col_names']); + $def['foreign keys'][$keyName] = array($fkey['table_name'], $colMap); } else { - $def['xunique indexes'][$keyName] = $cols; + $def['unique keys'][$keyName] = $cols; } } return $def; @@ -211,74 +193,67 @@ class PgsqlSchema extends Schema '(SELECT relname FROM pg_class WHERE oid=indexrelid) AS key_name, ' . '* FROM pg_index ' . 'WHERE indrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' . + 'AND indisprimary=\'f\' AND indisunique=\'f\' ' . 'ORDER BY indrelid, indexrelid'; $sql = sprintf($query, $table); return $this->fetchQueryData($sql); } /** - * - * Creates a table with the given names and columns. - * - * @param string $name Name of the table - * @param array $columns Array of ColumnDef objects - * for new table. - * - * @return boolean success flag + * Column names from the foreign table can be resolved with a call to getTableColumnNames() + * @param $table + * @return array array of rows with keys: fkey_name, table_name, table_id, col_names (array of strings) */ - - public function createTable($name, $columns) + function getForeignKeyInfo($table, $constraint_name) { - $uniques = array(); - $primary = array(); - $indices = array(); - $onupdate = array(); - - $sql = "CREATE TABLE $name (\n"; - - for ($i = 0; $i < count($columns); $i++) { - - $cd =& $columns[$i]; - - if ($i > 0) { - $sql .= ",\n"; - } - - $sql .= $this->_columnSql($cd); - switch ($cd->key) { - case 'UNI': - $uniques[] = $cd->name; - break; - case 'PRI': - $primary[] = $cd->name; - break; - case 'MUL': - $indices[] = $cd->name; - break; - } - } - - if (count($primary) > 0) { // it really should be... - $sql .= ",\n PRIMARY KEY (" . implode(',', $primary) . ")"; + // 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 ' . + '(SELECT relname FROM pg_class WHERE oid=confrelid) AS table_name, ' . + 'confrelid AS table_id, ' . + '(SELECT indkey FROM pg_index WHERE indexrelid=conindid) AS col_indexes ' . + 'FROM pg_constraint ' . + 'WHERE conrelid=(SELECT oid FROM pg_class WHERE relname=\'%s\') ' . + 'AND conname=\'%s\' ' . + 'AND contype=\'f\''; + $sql = sprintf($query, $table, $constraint_name); + $data = $this->fetchQueryData($sql); + if (count($data) < 1) { + throw new Exception("Could not find foreign key " . $constraint_name . " on table " . $table); } - $sql .= "); "; - - - foreach ($uniques as $u) { - $sql .= "\n CREATE index {$name}_{$u}_idx ON {$name} ($u); "; - } + $row = $data[0]; + return array( + 'table_name' => $row['table_name'], + 'col_names' => $this->getTableColumnNames($row['table_id'], $row['col_indexes']) + ); + } - foreach ($indices as $i) { - $sql .= "CREATE index {$name}_{$i}_idx ON {$name} ($i)"; + /** + * + * @param int $table_id + * @param array $col_indexes + * @return array of strings + */ + function getTableColumnNames($table_id, $col_indexes) + { + $indexes = array_map('intval', explode(' ', $col_indexes)); + $query = 'SELECT attnum AS col_index, attname AS col_name ' . + 'FROM pg_attribute where attrelid=%d ' . + 'AND attnum IN (%s)'; + $sql = sprintf($query, $table_id, implode(',', $indexes)); + $data = $this->fetchQueryData($sql); + + $byId = array(); + foreach ($data as $row) { + $byId[$row['col_index']] = $row['col_name']; } - $res = $this->conn->query($sql); - if (PEAR::isError($res)) { - throw new Exception($res->getMessage(). ' SQL was '. $sql); + $out = array(); + foreach ($indexes as $id) { + $out[] = $byId[$id]; } - - return true; + return $out; } /** @@ -297,31 +272,6 @@ class PgsqlSchema extends Schema return $type; } - /** - * Modifies a column in the schema. - * - * The name must match an existing column and table. - * - * @param string $table name of the table - * @param ColumnDef $columndef new definition of the column. - * - * @return boolean success flag - */ - - public function modifyColumn($table, $columndef) - { - $sql = "ALTER TABLE $table ALTER COLUMN TYPE " . - $this->_columnSql($columndef); - - $res = $this->conn->query($sql); - - if (PEAR::isError($res)) { - throw new Exception($res->getMessage()); - } - - return true; - } - /** * Return the proper SQL for creating or * altering a column. @@ -329,26 +279,25 @@ class PgsqlSchema extends Schema * Appropriate for use in CREATE TABLE or * ALTER TABLE statements. * - * @param string $tableName - * @param array $tableDef - * @param string $columnName * @param array $cd column to create * * @return string correct SQL for that column */ - function columnSql($name, array $cd) + function columnSql(array $cd) { $line = array(); - $line[] = parent::_columnSql($cd); + $line[] = parent::columnSql($cd); + /* if ($table['foreign keys'][$name]) { foreach ($table['foreign keys'][$name] as $foreignTable => $foreignColumn) { $line[] = 'references'; - $line[] = $this->quoteId($foreignTable); - $line[] = '(' . $this->quoteId($foreignColumn) . ')'; + $line[] = $this->quoteIdentifier($foreignTable); + $line[] = '(' . $this->quoteIdentifier($foreignColumn) . ')'; } } + */ return implode(' ', $line); } @@ -385,6 +334,20 @@ class PgsqlSchema extends Schema } } + /** + * Append an SQL statement to drop an index from a table. + * Note that in PostgreSQL, index names are DB-unique. + * + * @param array $statements + * @param string $table + * @param string $name + * @param array $def + */ + function appendDropIndex(array &$statements, $table, $name) + { + $statements[] = "DROP INDEX $name"; + } + /** * Quote a db/table/column identifier if necessary. * @@ -393,7 +356,7 @@ class PgsqlSchema extends Schema */ function quoteIdentifier($name) { - return '"' . $name . '"'; + return $this->conn->quoteIdentifier($name); } function mapType($column) @@ -408,12 +371,16 @@ class PgsqlSchema extends Schema $type = $map[$type]; } - if (!empty($column['size'])) { - $size = $column['size']; - if ($type == 'integer' && - in_array($size, array('small', 'big'))) { - $type = $size . 'int'; + if ($type == 'int') { + if (!empty($column['size'])) { + $size = $column['size']; + if ($size == 'small') { + return 'int2'; + } else if ($size == 'big') { + return 'int8'; + } } + return 'int4'; } return $type; @@ -431,24 +398,58 @@ class PgsqlSchema extends Schema } /** - * Map a native type back to an independent type + size + * Filter the given table definition array to match features available + * in this database. * - * @param string $type - * @return array ($type, $size) -- $size may be null + * This lets us strip out unsupported things like comments, foreign keys, + * or type variants that we wouldn't get back from getTableDef(). + * + * @param array $tableDef */ - protected function reverseMapType($type) + function filterDef(array $tableDef) { - $type = strtolower($type); - $map = array( - 'int4' => array('int', null), - 'int8' => array('int', 'big'), - 'bytea' => array('blob', null), - ); - if (isset($map[$type])) { - return $map[$type]; - } else { - return array($type, null); + foreach ($tableDef['fields'] as $name => &$col) { + // No convenient support for field descriptions + unset($col['description']); + + /* + if (isset($col['size'])) { + // Don't distinguish between tinyint and int. + if ($col['size'] == 'tiny' && $col['type'] == 'int') { + unset($col['size']); + } + } + */ + $col['type'] = $this->mapType($col); + unset($col['size']); + } + 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); + } } + return $tableDef; } + /** + * Filter the given key/index definition to match features available + * in this database. + * + * @param array $def + * @return array + */ + function filterKeyDef(array $def) + { + // PostgreSQL doesn't like prefix lengths specified on keys...? + foreach ($def as $i => $item) + { + if (is_array($item)) { + $def[$i] = $item[0]; + } + } + return $def; + } }