Some more poking at schema stuff, on the road towards a more portable table-modification with the new schema bits
This commit is contained in:
parent
efa8ff82f4
commit
2e475ceab0
@ -322,31 +322,6 @@ class MysqlSchema extends Schema
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look over a list of column definitions and list up which
|
||||
* indices will be present
|
||||
*/
|
||||
private function _indexList(array $columns)
|
||||
{
|
||||
$list = array('uniques' => array(),
|
||||
'primary' => array(),
|
||||
'indices' => array());
|
||||
foreach ($columns as $cd) {
|
||||
switch ($cd->key) {
|
||||
case 'UNI':
|
||||
$list['uniques'][] = $cd->name;
|
||||
break;
|
||||
case 'PRI':
|
||||
$list['primary'][] = $cd->name;
|
||||
break;
|
||||
case 'MUL':
|
||||
$list['indices'][] = $cd->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unique index key name for a given column on this table
|
||||
*/
|
||||
|
@ -208,93 +208,6 @@ class PgsqlSchema extends Schema
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Ensures that a table exists with the given
|
||||
* name and the given column definitions.
|
||||
*
|
||||
* If the table does not yet exist, it will
|
||||
* create the table. If it does exist, it will
|
||||
* alter the table to match the column definitions.
|
||||
*
|
||||
* @param string $tableName name of the table
|
||||
* @param array $columns array of ColumnDef
|
||||
* objects for the table
|
||||
*
|
||||
* @return boolean success flag
|
||||
*/
|
||||
|
||||
public function ensureTable($tableName, $columns)
|
||||
{
|
||||
// XXX: DB engine portability -> toilet
|
||||
|
||||
try {
|
||||
$td = $this->getTableDef($tableName);
|
||||
|
||||
} catch (Exception $e) {
|
||||
if (preg_match('/no such table/', $e->getMessage())) {
|
||||
return $this->createTable($tableName, $columns);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
$cur = $this->_names($td->columns);
|
||||
$new = $this->_names($columns);
|
||||
|
||||
$toadd = array_diff($new, $cur);
|
||||
$todrop = array_diff($cur, $new);
|
||||
$same = array_intersect($new, $cur);
|
||||
$tomod = array();
|
||||
foreach ($same as $m) {
|
||||
$curCol = $this->_byName($td->columns, $m);
|
||||
$newCol = $this->_byName($columns, $m);
|
||||
|
||||
|
||||
if (!$newCol->equals($curCol)) {
|
||||
// BIG GIANT TODO!
|
||||
// stop it detecting different types and trying to modify on every page request
|
||||
// $tomod[] = $newCol->name;
|
||||
}
|
||||
}
|
||||
if (count($toadd) + count($todrop) + count($tomod) == 0) {
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
|
||||
// For efficiency, we want this all in one
|
||||
// query, instead of using our methods.
|
||||
|
||||
$phrase = array();
|
||||
|
||||
foreach ($toadd as $columnName) {
|
||||
$cd = $this->_byName($columns, $columnName);
|
||||
|
||||
$phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
|
||||
}
|
||||
|
||||
foreach ($todrop as $columnName) {
|
||||
$phrase[] = 'DROP COLUMN ' . $columnName;
|
||||
}
|
||||
|
||||
foreach ($tomod as $columnName) {
|
||||
$cd = $this->_byName($columns, $columnName);
|
||||
|
||||
/* brute force */
|
||||
$phrase[] = 'DROP COLUMN ' . $columnName;
|
||||
$phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
||||
$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.
|
||||
@ -326,6 +239,49 @@ class PgsqlSchema extends Schema
|
||||
return implode(' ', $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append phrase(s) to an array of partial ALTER TABLE chunks in order
|
||||
* to alter the given column from its old state to a new one.
|
||||
*
|
||||
* @param array $phrase
|
||||
* @param string $columnName
|
||||
* @param array $old previous column definition as found in DB
|
||||
* @param array $cd current column definition
|
||||
*/
|
||||
function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
|
||||
{
|
||||
$prefix = 'ALTER COLUMN ' . $this->quoteIdentifier($columnName) . ' ';
|
||||
|
||||
$oldType = $this->mapType($old);
|
||||
$newType = $this->mapType($cd);
|
||||
if ($oldType != $newType) {
|
||||
$phrase[] .= $prefix . 'TYPE ' . $newType;
|
||||
}
|
||||
|
||||
if (!empty($old['not null']) && empty($cd['not null'])) {
|
||||
$phrase[] .= $prefix . 'DROP NOT NULL';
|
||||
} else if (empty($old['not null']) && !empty($cd['not null'])) {
|
||||
$phrase[] .= $prefix . 'SET NOT NULL';
|
||||
}
|
||||
|
||||
if (isset($old['default']) && !isset($cd['default'])) {
|
||||
$phrase[] . $prefix . 'DROP DEFAULT';
|
||||
} else if (!isset($old['default']) && isset($cd['default'])) {
|
||||
$phrase[] . $prefix . 'SET DEFAULT ' . $this->quoteDefaultValue($cd);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a db/table/column identifier if necessary.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
function quoteIdentifier($name)
|
||||
{
|
||||
return '"' . $name . '"';
|
||||
}
|
||||
|
||||
function mapType($column)
|
||||
{
|
||||
$map = array('serial' => 'bigserial', // FIXME: creates the wrong name for the sequence for some internal sequence-lookup function, so better fix this to do the real 'create sequence' dance.
|
||||
|
117
lib/schema.php
117
lib/schema.php
@ -345,12 +345,12 @@ class Schema
|
||||
* @return boolean success flag
|
||||
*/
|
||||
|
||||
public function ensureTable($tableName, $columns)
|
||||
public function ensureTable($tableName, $def)
|
||||
{
|
||||
// XXX: DB engine portability -> toilet
|
||||
|
||||
try {
|
||||
$td = $this->getTableDef($tableName);
|
||||
$old = $this->getTableDef($tableName);
|
||||
} catch (Exception $e) {
|
||||
if (preg_match('/no such table/', $e->getMessage())) {
|
||||
return $this->createTable($tableName, $columns);
|
||||
@ -359,20 +359,22 @@ class Schema
|
||||
}
|
||||
}
|
||||
|
||||
$cur = $this->_names($td->columns);
|
||||
$new = $this->_names($columns);
|
||||
$cur = array_keys($old['fields']);
|
||||
$new = array_keys($def['fields']);
|
||||
|
||||
$toadd = array_diff($new, $cur);
|
||||
$todrop = array_diff($cur, $new);
|
||||
$same = array_intersect($new, $cur);
|
||||
$tomod = array();
|
||||
|
||||
foreach ($same as $m) {
|
||||
$curCol = $this->_byName($td->columns, $m);
|
||||
$newCol = $this->_byName($columns, $m);
|
||||
// Find which fields have actually changed definition
|
||||
// in a way that we need to tweak them for this DB type.
|
||||
foreach ($same as $name) {
|
||||
$curCol = $old['fields'][$name];
|
||||
$newCol = $cur['fields'][$name];
|
||||
|
||||
if (!$newCol->equals($curCol)) {
|
||||
$tomod[] = $newCol->name;
|
||||
if (!$this->columnsEqual($curCol, $newCol)) {
|
||||
$tomod[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
@ -387,19 +389,18 @@ class Schema
|
||||
$phrase = array();
|
||||
|
||||
foreach ($toadd as $columnName) {
|
||||
$cd = $this->_byName($columns, $columnName);
|
||||
|
||||
$phrase[] = 'ADD COLUMN ' . $this->_columnSql($cd);
|
||||
$this->appendAlterAddColumn($phrase, $columnName,
|
||||
$def['fields'][$columnName]);
|
||||
}
|
||||
|
||||
foreach ($todrop as $columnName) {
|
||||
$phrase[] = 'DROP COLUMN ' . $columnName;
|
||||
$this->appendAlterModifyColumn($phrase, $columnName,
|
||||
$old['fields'][$columnName],
|
||||
$def['fields'][$columnName]);
|
||||
}
|
||||
|
||||
foreach ($tomod as $columnName) {
|
||||
$cd = $this->_byName($columns, $columnName);
|
||||
|
||||
$phrase[] = 'MODIFY COLUMN ' . $this->_columnSql($cd);
|
||||
$this->appendAlterDropColumn($phrase, $columnName);
|
||||
}
|
||||
|
||||
$sql = 'ALTER TABLE ' . $tableName . ' ' . implode(', ', $phrase);
|
||||
@ -413,6 +414,90 @@ class Schema
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append phrase(s) to an array of partial ALTER TABLE chunks in order
|
||||
* to add the given column definition to the table.
|
||||
*
|
||||
* @param array $phrase
|
||||
* @param string $columnName
|
||||
* @param array $cd
|
||||
*/
|
||||
function appendAlterAddColumn(array &$phrase, $columnName, array $cd)
|
||||
{
|
||||
$phrase[] = 'ADD COLUMN ' .
|
||||
$this->quoteIdentifier($columnName) .
|
||||
' ' .
|
||||
$this->columnSql($cd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append phrase(s) to an array of partial ALTER TABLE chunks in order
|
||||
* to alter the given column from its old state to a new one.
|
||||
*
|
||||
* @param array $phrase
|
||||
* @param string $columnName
|
||||
* @param array $old previous column definition as found in DB
|
||||
* @param array $cd current column definition
|
||||
*/
|
||||
function appendAlterModifyColumn(array &$phrase, $columnName, array $old, array $cd)
|
||||
{
|
||||
$phrase[] = 'MODIFY COLUMN ' .
|
||||
$this->quoteIdentifier($columnName) .
|
||||
' ' .
|
||||
$this->columnSql($cd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append phrase(s) to an array of partial ALTER TABLE chunks in order
|
||||
* to drop the given column definition from the table.
|
||||
*
|
||||
* @param array $phrase
|
||||
* @param string $columnName
|
||||
*/
|
||||
function appendAlterDropColumn(array &$phrase, $columnName)
|
||||
{
|
||||
$phrase[] = 'DROP COLUMN ' . $this->quoteIdentifier($columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quote a db/table/column identifier if necessary.
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
function quoteIdentifier($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
function quoteDefaultValue($cd)
|
||||
{
|
||||
if ($cd['type'] == 'datetime' && $cd['default'] == 'CURRENT_TIMESTAMP') {
|
||||
return $cd['default'];
|
||||
} else {
|
||||
return $this->quoteValue($cd['default']);
|
||||
}
|
||||
}
|
||||
|
||||
function quoteValue($val)
|
||||
{
|
||||
return $this->conn->escape($val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two column definitions are equivalent.
|
||||
* The default implementation checks _everything_ but in many cases
|
||||
* you may be able to discard a bunch of equivalencies.
|
||||
*
|
||||
* @param array $a
|
||||
* @param array $b
|
||||
* @return boolean
|
||||
*/
|
||||
function columnsEqual(array $a, array $b)
|
||||
{
|
||||
return !array_diff_assoc($a, $b) && !array_diff_assoc($b, $a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the array of names from an array of
|
||||
* ColumnDef objects.
|
||||
|
Loading…
Reference in New Issue
Block a user