diff --git a/phpgwapi/inc/adodb/adodb-datadict.inc.php b/phpgwapi/inc/adodb/adodb-datadict.inc.php index eb68903e59..c587dd27ff 100644 --- a/phpgwapi/inc/adodb/adodb-datadict.inc.php +++ b/phpgwapi/inc/adodb/adodb-datadict.inc.php @@ -167,7 +167,7 @@ class ADODB_DataDict { var $addCol = ' ADD'; var $alterCol = ' ALTER COLUMN'; var $dropCol = ' DROP COLUMN'; - var $renameCol = 'ALTER TABLE %s RENAME COLUMN %s TO %s'; // table, old-column, new-column, column-definitions (not used by default) + var $renameColumn = 'ALTER TABLE %s RENAME COLUMN %s TO %s'; // table, old-column, new-column, column-definitions (not used by default) var $nameRegex = '\w'; var $schema = false; var $serverInfo = array(); @@ -340,7 +340,18 @@ class ADODB_DataDict { return $sql; } - function AlterColumnSQL($tabname, $flds) + /** + * Change the definition of one column + * + * As some DBM's can't do that on there own, you need to supply the complete defintion of the new table, + * to allow, recreating the table and copying the content over to the new table + * @param string $tabname table-name + * @param string $flds column-name and type for the changed column + * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' + * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { $tabname = $this->TableName ($tabname); $sql = array(); @@ -352,7 +363,16 @@ class ADODB_DataDict { return $sql; } - // $flds is only used for mysql so far + /** + * Rename one column + * + * Some DBM's can only do this together with changeing the type of the column (even if that stays the same, eg. mysql) + * @param string $tabname table-name + * @param string $oldcolumn column-name to be renamed + * @param string $newcolumn new column-name + * @param string $flds='' complete column-defintion-string like for AddColumnSQL, only used by mysql atm., default='' + * @return array with SQL strings + */ function RenameColumnSQL($tabname,$oldcolumn,$newcolumn,$flds='') { $tabname = $this->TableName ($tabname); @@ -364,7 +384,18 @@ class ADODB_DataDict { return array(sprintf($this->renameColumn,$tabname,$this->NameQuote($oldcolumn),$this->NameQuote($newcolumn),$column_def)); } - function DropColumnSQL($tabname, $flds) + /** + * Drop one column + * + * Some DBM's can't do that on there own, you need to supply the complete defintion of the new table, + * to allow, recreating the table and copying the content over to the new table + * @param string $tabname table-name + * @param string $flds column-name and type for the changed column + * @param string $tableflds='' complete defintion of the new table, eg. for postgres, default '' + * @param array/string $tableoptions='' options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { $tabname = $this->TableName ($tabname); if (!is_array($flds)) $flds = explode(',',$flds); diff --git a/phpgwapi/inc/adodb/datadict/datadict-postgres.inc.php b/phpgwapi/inc/adodb/datadict/datadict-postgres.inc.php index 52cd0473ec..93c2735d69 100644 --- a/phpgwapi/inc/adodb/datadict/datadict-postgres.inc.php +++ b/phpgwapi/inc/adodb/datadict/datadict-postgres.inc.php @@ -20,7 +20,7 @@ class ADODB2_postgres extends ADODB_DataDict { var $seqPrefix = 'SEQ_'; var $addCol = ' ADD COLUMN'; var $quote = '"'; - var $renameTable = 'ALTER TABLE %s RENAME TO %s'; + var $renameTable = 'ALTER TABLE %s RENAME TO %s'; // at least since 7.1 function MetaType($t,$len=-1,$fieldobj=false) { @@ -112,25 +112,111 @@ class ADODB2_postgres extends ADODB_DataDict { } } - /* The following does not work in Pg 6.0 - does anyone want to contribute code? - - //"ALTER TABLE table ALTER COLUMN column SET DEFAULT mydef" and - //"ALTER TABLE table ALTER COLUMN column DROP DEFAULT mydef" - //"ALTER TABLE table ALTER COLUMN column SET NOT NULL" and - //"ALTER TABLE table ALTER COLUMN column DROP NOT NULL"*/ - function AlterColumnSQL($tabname, $flds) + /** + * Change the definition of one column + * + * Postgres can't do that on it's own, you need to supply the complete defintion of the new table, + * to allow, recreating the table and copying the content over to the new table + * @param string $tabname table-name + * @param string $flds column-name and type for the changed column + * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' + * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function AlterColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { - if ($this->debug) ADOConnection::outp("AlterColumnSQL not supported for PostgreSQL"); - return array(); + if (!$tableflds) { + if ($this->debug) ADOConnection::outp("AlterColumnSQL needs a complete table-definiton for PostgreSQL"); + return array(); + } + return $this->_recreate_copy_table($tabname,False,$tableflds,$tableoptions); } - - function DropColumnSQL($tabname, $flds) + /** + * Drop one column + * + * Postgres < 7.3 can't do that on it's own, you need to supply the complete defintion of the new table, + * to allow, recreating the table and copying the content over to the new table + * @param string $tabname table-name + * @param string $flds column-name and type for the changed column + * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' + * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function DropColumnSQL($tabname, $flds, $tableflds='',$tableoptions='') { - if ($this->debug) ADOConnection::outp("DropColumnSQL only works with PostgreSQL 7.3+"); - return ADODB_DataDict::DropColumnSQL($tabname, $flds)."/* only works for PostgreSQL 7.3+ */"; + $has_drop_column = 7.3 <= (float) $this->serverInfo['version']; + if (!$has_drop_column && !$tableflds) { + if ($this->debug) ADOConnection::outp("DropColumnSQL needs complete table-definiton for PostgreSQL < 7.3"); + return array(); + } + if ($has_drop_column) { + return ADODB_DataDict::DropColumnSQL($tabname, $flds); + } + return $this->_recreate_copy_table($tabname,$flds,$tableflds,$tableoptions); } - + + /** + * Save the content into a temp. table, drop and recreate the original table and copy the content back in + * + * We also take care to set the values of the sequenz and recreate the indexes. + * All this is done in a transaction, to not loose the content of the table, if something went wrong! + * @internal + * @param string $tabname table-name + * @param string $dropflds column-names to drop + * @param string $tableflds complete defintion of the new table, eg. for postgres, default '' + * @param array/ $tableoptions options for the new table see CreateTableSQL, default '' + * @return array with SQL strings + */ + function _recreate_copy_table($tabname,$dropflds,$tableflds,$tableoptions='') + { + if ($dropflds && !is_array($dropflds)) $dropflds = explode(',',$dropflds); + $copyflds = array(); + foreach($this->MetaColumns($tabname) as $fld) { + if (!$dropflds || !in_array($fld->name,$dropflds)) { + $copyflds[] = $fld->name; + // identify the sequenze name and the fld its on + if ($fld->primary_key && $fld->has_default && + preg_match("/nextval\('([^']+)'::text\)/",$fld->default_value,$matches)) { + $seq_name = $matches[1]; + $seq_fld = $fld->name; + } + } + } + $copyflds = implode(',',$copyflds); + + $tempname = $tabname.'_tmp'; + $aSql[] = 'BEGIN'; // we use a transaction, to make sure not to loose the content of the table + $aSql[] = "SELECT * INTO TEMPORARY TABLE $tempname FROM $tabname"; + $aSql = array_merge($aSql,$this->DropTableSQL($tabname)); + $aSql = array_merge($aSql,$this->CreateTableSQL($tabname,$tableflds,$tableoptions)); + $aSql[] = "INSERT INTO $tabname SELECT $copyflds FROM $tempname"; + if ($seq_name && $seq_fld) { // if we have a sequence we need to set it again + $aSql[] = "SELECT setval('$seq_name',MAX($seq_fld)) FROM $tabname"; + } + $aSql[] = "DROP TABLE $tempname"; + // recreate the indexes, if they not contain one of the droped columns + foreach($this->MetaIndexes($tabname) as $idx_name => $idx_data) + { + if (substr($idx_name,-5) != '_pkey' && (!$dropflds || !count(array_intersect($dropflds,$idx_data['columns'])))) { + $aSql = array_merge($aSql,$this->CreateIndexSQL($idx_name,$tabname,$idx_data['columns'], + $idx_data['unique'] ? array('UNIQUE') : False)); + } + } + $aSql[] = 'COMMIT'; + return $aSql; + } + + function DropTableSQL($tabname) + { + $sql = ADODB_DataDict::DropTableSQL($tabname); + + $drop_seq = $this->_DropAutoIncrement($tabname); + if ($drop_seq) $sql[] = $drop_seq; + + return $sql; + } + // return string must begin with space function _CreateSuffix($fname, &$ftype, $fnotnull,$fdefault,$fautoinc,$fconstraint) { @@ -145,9 +231,15 @@ class ADODB2_postgres extends ADODB_DataDict { return $suffix; } - function _DropAutoIncrement($t) + // search for a sequece for the given table (asumes the seqence-name contains the table-name!) + // if yes return sql to drop it + // this is still necessary if postgres < 7.3 or the SERIAL was created on an earlier version!!! + function _DropAutoIncrement($tabname) { - return "drop sequence ".$t."_m_id_seq"; + return False; // we should/can only drop a sequenz is NOT automaticaly droped !!! + $tabname = $this->connection->quote('%'.$tabname.'%'); + $seq = $this->connection->GetOne("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relname LIKE $tabname AND relkind='S' ORDER BY relname"); + return $seq ? "DROP SEQUENCE ".$seq : false; } /*