2004-07-03 13:00:24 +02:00
< ? php
2004-08-13 21:01:03 +02:00
/************************************************************************** \
* eGroupWare - Setup - db - schema - processor *
* http :// www . egroupware . org *
* -------------------------------------------- *
* Rewritten and adopted to ADOdb ' s data - dictionary by Ralf Becker *
* < RalfBecker @ outdoor - training . de > *
* This file was originaly written by *
* - Michael Dean < mdean @ users . sourceforge . net > *
* - Miles Lott < milosch @ groupwhere . org > *
* -------------------------------------------- *
* This program is free software ; you can redistribute it and / or modify it *
* under the terms of the GNU General Public License as published by the *
* Free Software Foundation ; either version 2 of the License , or ( at your *
* option ) any later version . *
\ **************************************************************************/
/* $Id$ */
/**
* eGW ' s ADOdb based schema - processor
*
* @ class schema_proc
* @ author RalfBecker - AT - outdoor - training . de and others
* @ license GPL
*/
2002-03-03 22:48:34 +01:00
class schema_proc
{
var $m_oTranslator ;
var $m_oDeltaProc ;
var $m_odb ;
2004-08-13 21:01:03 +02:00
var $m_aTables ; // stores the table-definitions of an application, set by ExecuteScripts
2002-03-03 22:48:34 +01:00
var $m_bDeltaOnly ;
2004-08-13 21:01:03 +02:00
var $debug = 0 ; // 0=Off, 1=some, eg. primary function calls, 2=lots incl. the SQL used
var $max_index_length = array ( // if known
'sapdb' => 32 ,
);
var $sType ; // type of the database, set by the the constructor
/**
* Constructor of schema - processor
*
* @ param string $dbms type of the database : 'mysql' , 'pgsql' , 'mssql' , 'sapdb'
*/
function schema_proc ( $dbms , $aTables = False )
2002-03-03 22:48:34 +01:00
{
2003-10-21 20:09:58 +02:00
$this -> sType = $dbms ;
2002-03-03 22:48:34 +01:00
$this -> m_aTables = array ();
$this -> m_bDeltaOnly = False ; // Default to false here in case it's just a CreateTable script
2004-08-13 21:01:03 +02:00
2004-08-15 17:43:52 +02:00
$this -> m_odb = is_object ( $GLOBALS [ 'phpgw' ] -> db ) ? $GLOBALS [ 'phpgw' ] -> db : $GLOBALS [ 'phpgw_setup' ] -> db ;
$this -> m_odb -> connect ();
2004-08-13 21:01:03 +02:00
$this -> adodb = & $GLOBALS [ 'phpgw' ] -> ADOdb ;
$this -> dict = NewDataDictionary ( $this -> adodb );
// enable the debuging in ADOdb's datadictionary if the debug-level is greater then 1
if ( $this -> debug > 1 ) $this -> dict -> debug = True ;
// to allow some of the former translator-functions to be called, we assign ourself as the translator
$this -> m_oTranslator = & $this ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Created a table named $sTableName as defined in $aTableDef
*
* @ param string $sTableName
* @ param array $aTableDef
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
function CreateTable ( $sTableName , $aTableDef )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( $this -> debug )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$this -> debug_message ( 'schema_proc::CreateTable(%1,%2)' , False , $sTableName , $aTableDef );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
$this -> m_aTables [ $sTableName ] = $aTableDef ;
if ( $this -> m_bDeltaOnly ) return true ;
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
// creating the table
$aSql = $this -> dict -> CreateTableSQL ( $sTableName , $ado_cols = $this -> _egw2adodb_columndef ( $aTableDef ));
if ( ! ( $retVal = $this -> ExecuteSQLArray ( $aSql , 2 , 'CreateTableSQL(%1,%2) sql=%3' , False , $sTableName , $ado_cols , $aSql )))
{
return $retVal ;
}
// creating unique indices/constrains
foreach ( $aTableDef [ 'uc' ] as $name => $mFields )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( is_numeric ( $name ))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$name = $this -> _index_name ( $sTableName , $mFields );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
$aSql = $this -> dict -> CreateIndexSQL ( $name , $sTableName , $mFields , array ( 'UNIQUE' ));
if ( ! ( $retVal = $this -> ExecuteSQLArray ( $aSql , 2 , 'CreateIndexSql(%1,%2,%3,%4) sql=%5' , False , $name , $sTableName , $mFields , array ( 'UNIQUE' ), $aSql )))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
return $retVal ;
2002-03-03 22:48:34 +01:00
}
}
2004-08-13 21:01:03 +02:00
// creation indices
foreach ( $aTableDef [ 'ix' ] as $name => $mFields )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$options = False ;
if ( is_array ( $mFields ))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( isset ( $mFields [ 'options' ])) // array sets additional options
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( isset ( $mFields [ 'options' ][ $this -> sType ]))
{
$options = $mFields [ 'options' ][ $this -> sType ]; // db-specific options, eg. index-type
if ( ! $options ) continue ; // no index for our db-type
}
unset ( $mFields [ 'options' ]);
2002-03-03 22:48:34 +01:00
}
}
2004-08-13 21:01:03 +02:00
if ( is_numeric ( $name ))
{
$name = $this -> _index_name ( $sTableName , $mFields );
}
$aSql = $this -> dict -> CreateIndexSQL ( $name , $sTableName , $mFields , array ( $options ));
if ( ! ( $retVal = $this -> ExecuteSQLArray ( $aSql , 2 , 'CreateIndexSql(%1,%2,%3,%4) sql=%5' , False , $name , $sTableName , $mFields , $options , $aSql )))
{
return $retVal ;
2002-03-03 22:48:34 +01:00
}
}
2004-08-13 21:01:03 +02:00
return $retVal ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Drops all tables in $aTables
*
* @ param array $aTables array of eGW table - definitions
* @ param boolean $bOutputHTML should we give diagnostics , default False
* @ return boolean True if no error , else False
*/
2002-03-03 22:48:34 +01:00
function DropAllTables ( $aTables , $bOutputHTML = False )
{
if ( ! is_array ( $aTables ) || ! isset ( $this -> m_odb ))
{
return False ;
}
2004-08-13 21:01:03 +02:00
// set our debug-mode or $bOutputHTML is the other one is set
if ( $this -> debug ) $bOutputHTML = True ;
if ( $bOutputHTML && ! $this -> debug ) $this -> debug = 2 ;
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
$this -> m_aTables = array ();
if ( $this -> m_bDeltaOnly ) return true ;
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
foreach ( $aTables as $sTableName => $aTableDef )
2002-03-03 22:48:34 +01:00
{
if ( $this -> DropTable ( $sTableName ))
{
if ( $bOutputHTML )
{
echo '<br>Drop Table <b>' . $sTableSQL . '</b>' ;
}
}
else
{
return False ;
}
}
return True ;
}
2004-08-13 21:01:03 +02:00
/**
* Drops the table $sTableName
*
* @ param string $sTableName
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
2002-03-03 22:48:34 +01:00
function DropTable ( $sTableName )
{
2004-08-13 21:01:03 +02:00
unset ( $this -> m_aTables [ $sTableName ]);
if ( $this -> m_bDeltaOnly ) return True ;
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
if ( $this -> sType == 'pgsql' ) $this -> _PostgresTestDropOldSequence ( $sTableName );
$aSql = $this -> dict -> DropTableSql ( $sTableName );
return $this -> ExecuteSQLArray ( $aSql , 2 , 'DropTable(%1) sql=%2' , False , $sTableName , $aSql );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Drops column $sColumnName from table $sTableName
*
* @ param string $sTableName table - name
* @ param array $aTableDef eGW table - defintion
* @ param string $sColumnName column - name
* @ param boolean $bCopyData ? ? ?
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
2002-03-03 22:48:34 +01:00
function DropColumn ( $sTableName , $aTableDef , $sColumnName , $bCopyData = true )
{
2004-08-13 21:01:03 +02:00
unset ( $this -> m_aTables [ $sTableName ][ 'fd' ][ $sColumnName ]);
if ( $this -> m_bDeltaOnly ) return True ;
$aSql = $this -> dict -> DropColumnSql ( $sTableName , $sColumnName , $ado_table = $this -> _egw2adodb_columndef ( $this -> m_aTables [ $sTableName ]));
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
return $this -> ExecuteSQLArray ( $aSql , 2 , 'DropColumnSQL(%1,%2,%3) sql=%4' , False , $sTableName , $sColumnName , $ado_table , $aSql );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Renames table $sOldTableName to $sNewTableName
*
* @ param string $sOldTableName old ( existing ) table - name
* @ param string $sNewTableName new table - name
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
2002-03-03 22:48:34 +01:00
function RenameTable ( $sOldTableName , $sNewTableName )
{
2004-08-13 21:01:03 +02:00
$this -> m_aTables [ $sNewTableName ] = $this -> m_aTables [ $sOldTableName ];
unset ( $this -> m_aTables [ $sOldTableName ]);
if ( $this -> m_bDeltaOnly ) return True ;
if ( $this -> sType == 'pgsql' ) $this -> _PostgresTestDropOldSequence ( $sTableName );
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
$aSql = $this -> dict -> RenameTableSQL ( $sOldTableName , $sNewTableName );
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
return $this -> ExecuteSQLArray ( $aSql , 2 , 'RenameTableSQL(%1,%2) sql=%3' , False , $sOldTableName , $sNewTableName , $aSql );
}
/**
* Check if we have an old , not automaticaly droped sequence and drop it
*
*
* @ param $sTableName
*/
function _PostgresTestDropOldSequence ( $sTableName )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( ! $this -> sType == 'pgsql' ) return ;
$seq = $this -> adodb -> GetOne ( " SELECT a.attname FROM pg_attribute a, pg_class c, pg_attrdef d WHERE c.relname=' $sTableName ' AND c.oid=d.adrelid AND d.adsrc LIKE '%seq_ $sTableName %' AND a.attrelid=c.oid AND d.adnum=a.attnum " );
if ( $seq )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$this -> query ( " DROP SEQUENCE $seq " , __LINE__ , __FILE__ );
2002-03-03 22:48:34 +01:00
}
}
2004-08-13 21:01:03 +02:00
/**
* Changes one ( exiting ) column in a table
*
* @ param string $sTableName table - name
* @ param string $sColumnName column - name
* @ param array $aColumnDef new column - definition
* @ param boolean $bCopyData ? ? ?
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
2002-03-03 22:48:34 +01:00
function AlterColumn ( $sTableName , $sColumnName , $aColumnDef , $bCopyData = True )
{
2004-08-13 21:01:03 +02:00
$this -> m_aTables [ $sTableName ][ 'fd' ][ $sColumnName ] = $aColumnDef ;
if ( $this -> m_bDeltaOnly ) return True ;
$aSql = $this -> dict -> AlterColumnSQL ( $sTableName , $ado_col = $this -> _egw2adodb_columndef ( array (
'fd' => array ( $sColumnName => $aColumnDef ),
'pk' => array (),
)), $ado_table = $this -> _egw2adodb_columndef ( $this -> m_aTables [ $sTableName ]));
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
return $this -> ExecuteSQLArray ( $aSql , 2 , 'AlterColumnSQL(%1,%2,%3) sql=%4' , False , $sTableName , $ado_col , $ado_table , $aSql );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Renames column $sOldColumnName to $sNewColumnName in table $sTableName
*
* @ param string $sTableName table - name
* @ param string $sOldColumnName old ( existing ) column - name
* @ param string $sNewColumnName new column - name
* @ param boolean $bCopyData ? ? ?
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
function RenameColumn ( $sTableName , $sOldColumnName , $sNewColumnName , $bCopyData = True )
{
$this -> m_aTables [ $sTableName ][ 'fd' ][ $sNewColumnName ] = $this -> m_aTables [ $sTableName ][ 'fd' ][ $sOldColumnName ];
unset ( $this -> m_aTables [ $sTableName ][ 'fd' ][ $sOldColumnName ]);
if ( $this -> m_bDeltaOnly ) return True ;
// we have to use the new column-name, as m_oDeltaProc has already changed m_aTables
$col_def = $this -> _egw2adodb_columndef ( array (
'fd' => array ( $sNewColumnName => $this -> m_aTables [ $sTableName ][ 'fd' ][ $sNewColumnName ]),
'pk' => array (),
));
$aSql = $this -> dict -> RenameColumnSQL ( $sTableName , $sOldColumnName , $sNewColumnName , $col_def );
return $this -> ExecuteSQLArray ( $aSql , 2 , 'RenameColumnSQL(%1,%2,%3) sql=%4' , False , $sTableName , $sOldColumnName , $sNewColumnName , $aSql );
}
/**
* Add one ( new ) column to a table
*
* @ param string $sTableName table - name
* @ param string $sColumnName column - name
* @ param array $aColumnDef column - definition
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
2002-03-03 22:48:34 +01:00
function AddColumn ( $sTableName , $sColumnName , $aColumnDef )
{
2004-08-13 21:01:03 +02:00
$this -> m_aTables [ $sTableName ][ 'fd' ][ $sColumnName ] = $aColumnDef ;
if ( $this -> m_bDeltaOnly ) return True ;
$aSql = $this -> dict -> AddColumnSQL ( $sTableName , $ado_cols = $this -> _egw2adodb_columndef ( array (
'fd' => array ( $sColumnName => $aColumnDef ),
'pk' => array (),
)));
return $this -> ExecuteSQLArray ( $aSql , 2 , 'AlterColumnSQL(%1,%2,%3) sql=%4' , False , $sTableName , $sColumnName , $aColumnDef , $aSql );
}
/**
* Create an ( unique ) Index over one or more columns
*
* @ param string $sTablename table - name
* @ param array $aColumnNames columns for the index
* @ param boolean $bUnique = false true for a unique index , default false
* @ param array / string $options = '' db - sepecific options , default '' = none
* @ param string $sIdxName = '' name of the index , if not given ( default ) its created automaticaly
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
function CreateIndex ( $sTableName , $aColumnNames , $type = False , $options = '' , $sIdxName = '' )
{
$kind = $bUnique ? 'uc' : 'ix' ;
$key = ! $sIdxName || is_numeric ( $sIdxName ) ? count ( $this -> m_aTables [ $sTableName ][ $kind ]) : $sIdxName ;
$this -> m_aTables [ $sTableName ][ $kind ][ $key ] = $aColumnNames ;
if ( $this -> m_bDeltaOnly ) return true ;
if ( ! $sIdxName || is_numeric ( $sIdxName ))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$sIdxName = $this -> _index_name ( $sTableName , $aColumnNames );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
$aSql = $this -> dict -> CreateIndexSQL ( $name , $sTableName , $aColumnNames , is_array ( $options ) ? $options : array ( $options ));
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
return $this -> ExecuteSQLArray ( $aSql , 2 , 'CreateIndexSQL(%1,%2,%3) sql=%4' , False , $name , $sTableName , $sColumnName , $aColumnDef , $aSql );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Drop an Index
*
* @ param string $sTablename table - name
* @ param array / string $aColumnNames columns of the index or the name of the index
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
function DropIndex ( $sTableName , $aColumnNames )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( is_array ( $aColumnNames ))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
foreach ( array ( 'ix' , 'uc' ) as $kind )
{
foreach ( $this -> m_aTables [ $sTableName ][ $kind ] as $idx => $columns )
{
if ( ! is_array ( $columns )) $columns = array ( $columns );
unset ( $columns [ 'options' ]);
if ( implode ( ':' , $columns ) == implode ( ':' , $aColumnNames ))
{
unset ( $this -> m_aTables [ $sTableName ][ $kind ][ $idx ]);
break ;
}
}
}
$indexes = $this -> dict -> MetaIndexes ( $sTableName );
if ( $indexes === False )
{
// if MetaIndexes is not availible for the DB, we try the name the index was created with
// this fails if one of the columns have been renamed
$sIdxName = $this -> _index_name ( $sTableName , $aColumnNames );
}
else
{
foreach ( $this -> dict -> MetaIndexes ( $sTableName ) as $idx => $idx_data )
{
if ( strtolower ( implode ( ':' , $idx_data [ 'columns' ])) == implode ( ':' , $aColumnNames ))
{
$sIdxName = $idx ;
break ;
}
}
}
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
else
{
$sIdxName = $aColumnNames ;
}
if ( $this -> m_bDeltaOnly || ! $sIdxName )
{
return True ;
}
$aSql = $this -> dict -> DropIndexSQL ( $sIdxName , $sTableName );
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
return $this -> ExecuteSQLArray ( $aSql , 2 , 'DropIndexSQL(%1(%2),%3) sql=%4' , False , $sIdxName , $aColumnNames , $sTableName , $aSql );
}
/**
* Updating the sequence - value , after eg . copying data via RefreshTable
* @ param string $sTableName table - name
* @ param string $sColumnName column - name , which default is set to nextval ()
*/
2004-07-03 00:12:10 +02:00
function UpdateSequence ( $sTableName , $sColumnName )
{
2004-08-13 21:01:03 +02:00
switch ( $this -> sType )
2004-07-03 00:12:10 +02:00
{
2004-08-13 21:01:03 +02:00
case 'pgsql' :
// identify the sequence name, ADOdb uses a different name or it might be renamed
$columns = $this -> dict -> MetaColumns ( $sTableName );
$seq_name = 'seq_' . $sTableName ;
if ( preg_match ( " /nextval \ ('([^']+)'::text \ )/ " , $columns [ $sColumnName ] -> default_value , $matches ))
{
$seq_name = $matches [ 1 ];
}
$sql = " SELECT setval(' $seq_name ',MAX( $sColumnName ) FROM $sTableName " ;
if ( $GLOBALS [ 'DEBUG' ]) { echo " <br>Updating sequence ' $seq_name using: $sql " ; }
return $this -> query ( $sql , __LINE__ , __FILE__ );
2004-07-03 00:12:10 +02:00
}
return True ;
}
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
/**
* This function manually re - created the table incl . primary key and all other indices
*
* It is meant to use if the primary key , existing indices or column - order changes or
* columns are not longer used or new columns need to be created ( with there default value or NULL )
* Beside the default - value in the schema , one can give extra defaults via $aDefaults to eg . use an
* other colum or function to set the value of a new or changed column
*
* @ param string $sTableName table - name
* @ param array $aTableDef eGW table - defintion
* @ param array / boolean $aDefaults array with default for the colums during copying , values are either ( old ) column - names or quoted string - literals
*/
2004-02-29 01:13:47 +01:00
function RefreshTable ( $sTableName , $aTableDef , $aDefaults = False )
2004-02-19 09:48:15 +01:00
{
2004-05-24 16:23:46 +02:00
if ( $GLOBALS [ 'DEBUG' ]) { echo " <p>schema_proc::RefreshTable(' $sTableName ', " . _debug_array ( $aTableDef , False ) . " )<p>m_aTables[ $sTableName ]= " . _debug_array ( $this -> m_aTables [ $sTableName ], False ) . " \n " ; }
2004-02-19 09:48:15 +01:00
$old_fd = $this -> m_aTables [ $sTableName ][ 'fd' ];
2004-08-13 21:01:03 +02:00
$this -> m_aTables [ $sTableName ] = $aTableDef ;
if ( $this -> m_bDeltaOnly ) return true ;
2004-02-19 09:48:15 +01:00
$tmp_name = 'tmp_' . $sTableName ;
$this -> m_odb -> transaction_begin ();
$select = array ();
2004-08-13 21:01:03 +02:00
$blob_column_included = $auto_column_included = False ;
2004-02-19 09:48:15 +01:00
foreach ( $aTableDef [ 'fd' ] as $name => $data )
{
2004-02-29 01:13:47 +01:00
if ( $aDefaults && isset ( $aDefaults [ $name ])) // use given default
{
2004-06-27 11:53:58 +02:00
$value = $aDefaults [ $name ];
2004-02-29 01:13:47 +01:00
}
elseif ( isset ( $old_fd [ $name ])) // existing column, use its value => column-name in query
2004-02-19 09:48:15 +01:00
{
2004-06-27 11:53:58 +02:00
$value = $name ;
2004-02-19 09:48:15 +01:00
}
else // new column => use default value or NULL
{
if ( ! isset ( $data [ 'default' ]) && ( ! isset ( $data [ 'nullable' ]) || $data [ 'nullable' ]))
{
2004-06-27 11:53:58 +02:00
$value = 'NULL' ;
2004-02-19 09:48:15 +01:00
}
else
{
2004-06-27 11:53:58 +02:00
$value = $this -> m_odb -> quote ( isset ( $data [ 'default' ]) ? $data [ 'default' ] : '' , $data [ 'type' ]);
// fix for postgres error "no '<' operator for type 'unknown'"
if ( $this -> sType == 'pgsql' )
{
$type_translated = $this -> m_oTranslator -> TranslateType ( $data [ 'type' ]);
$value = " CAST( $value AS $type_translated ) " ;
}
2004-02-19 09:48:15 +01:00
}
}
2004-08-13 21:01:03 +02:00
$blob_column_included = $blob_column_included || in_array ( $data [ 'type' ], array ( 'blob' , 'text' , 'longtext' ));
$auto_column_included = $auto_column_included || $data [ 'type' ] == 'auto' ;
2004-06-27 11:53:58 +02:00
$select [] = $value ;
2004-02-19 09:48:15 +01:00
}
$select = implode ( ',' , $select );
2004-07-03 00:12:10 +02:00
2004-08-13 21:01:03 +02:00
$extra = '' ;
$distinct = 'DISTINCT' ;
switch ( $this -> sType )
{
case 'mssql' :
if ( $auto_column_included ) $extra = " SET IDENTITY_INSERT $sTableName ON \n " ;
if ( $blob_column_included ) $distinct = '' ; // no distinct on blob-columns
break ;
}
// because of all the trouble with sequences and indexes in the global namespace,
// we use an additional temp. table for postgres and not rename the existing on, but drop it.
if ( $this -> sType == 'pgsql' )
{
$Ok = $this -> m_odb -> query ( " SELECT * INTO TEMPORARY TABLE $tmp_name FROM $sTableName " , __LINE__ , __FILE__ ) &&
$this -> DropTable ( $sTableName );
}
else
{
$Ok = $this -> RenameTable ( $sTableName , $tmp_name );
}
$Ok = $Ok && $this -> CreateTable ( $sTableName , $aTableDef ) &&
$this -> m_odb -> query ( " $extra INSERT INTO $sTableName ( " .
implode ( ',' , array_keys ( $aTableDef [ 'fd' ])) .
" ) SELECT $distinct $select FROM $tmp_name " , __LINE__ , __FILE__ ) &&
$this -> DropTable ( $tmp_name );
2004-02-19 09:48:15 +01:00
if ( ! $Ok )
{
2004-04-03 12:22:08 +02:00
$this -> m_odb -> transaction_abort ();
2004-02-19 09:48:15 +01:00
return False ;
}
2004-07-03 00:12:10 +02:00
// do we need to update the new sequences value ?
if ( count ( $aTableDef [ 'pk' ]) == 1 && $aTableDef [ 'fd' ][ $aTableDef [ 'pk' ][ 0 ]][ 'type' ] == 'auto' )
{
$this -> UpdateSequence ( $sTableName , $aTableDef [ 'pk' ][ 0 ]);
}
2004-02-19 09:48:15 +01:00
$this -> DropTable ( $tmp_name );
$this -> m_odb -> transaction_commit ();
return True ;
}
2004-08-13 21:01:03 +02:00
/**
* depricated Function does nothing any more
* @ depricated
*/
function GenerateScripts ( $aTables , $bOutputHTML = False )
{
return True ;
}
/**
* Creates all tables for one application
*
* @ param array $aTables array of eGW table - definitions
* @ param boolean $bOutputHTML should we give diagnostics , default False
* @ return boolean True on success , False if an ( fatal ) error occured
*/
function ExecuteScripts ( $aTables , $bOutputHTML = False )
{
if ( ! is_array ( $aTables ) || ! IsSet ( $this -> m_odb ))
{
return False ;
}
// set our debug-mode or $bOutputHTML is the other one is set
if ( $this -> debug ) $bOutputHTML = True ;
if ( $bOutputHTML && ! $this -> debug ) $this -> debug = 2 ;
$this -> m_aTables = $aTables ;
foreach ( $aTables as $sTableName => $aTableDef )
{
if ( $this -> CreateTable ( $sTableName , $aTableDef ))
{
if ( $bOutputHTML )
{
echo '<br>Create Table <b>' . $sTableName . '</b>' ;
}
}
else
{
if ( $bOutputHTML )
{
echo '<br>Create Table Failed For <b>' . $sTableName . '</b>' ;
}
return False ;
}
}
return True ;
}
/**
* Return the value of a column
*
* @ param string / integer $Name name of field or positional index starting from 0
* @ param bool $strip_slashes string escape chars from field ( optional ), default false
* @ return string the field value
*/
function f ( $value , $strip_slashes = False )
2002-03-03 22:48:34 +01:00
{
if ( $this -> m_bDeltaOnly )
{
// Don't care, since we are processing deltas only
return False ;
}
2004-08-13 21:01:03 +02:00
return $this -> m_odb -> f ( $value , $strip_slashes );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Number of rows in current result set
*
* @ return int number of rows
*/
2002-03-03 22:48:34 +01:00
function num_rows ()
{
if ( $this -> m_bDeltaOnly )
{
// If not False, we will cause while loops calling us to hang
return False ;
}
return $this -> m_odb -> num_rows ();
}
2004-08-13 21:01:03 +02:00
/**
* Move to the next row in the results set
*
* @ return bool was another row found ?
*/
2002-03-03 22:48:34 +01:00
function next_record ()
{
if ( $this -> m_bDeltaOnly )
{
// If not False, we will cause while loops calling us to hang
return False ;
}
return $this -> m_odb -> next_record ();
}
2004-08-13 21:01:03 +02:00
/**
* Execute a query
*
* @ param string $Query_String the query to be executed
* @ param mixed $line the line method was called from - use __LINE__
* @ param string $file the file method was called from - use __FILE__
* @ param int $offset row to start from
* @ param int $num_rows number of rows to return ( optional ), if unset will use $GLOBALS [ 'phpgw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ]
* @ return ADORecordSet or false , if the query fails
*/
2002-03-03 22:48:34 +01:00
function query ( $sQuery , $line = '' , $file = '' )
{
if ( $this -> m_bDeltaOnly )
{
// Don't run this query, since we are processing deltas only
return True ;
}
return $this -> m_odb -> query ( $sQuery , $line , $file );
}
2004-08-13 21:01:03 +02:00
/**
* Insert a row of data into a table or updates it if $where is given , all data is quoted according to it ' s type
*
* @ param string $table name of the table
* @ param array $data with column - name / value pairs
* @ param mixed $where string with where clause or array with column - name / values pairs to check if a row with that keys already exists , or false for an unconditional insert
* if the row exists db :: update is called else a new row with $date merged with $where gets inserted ( data has precedence )
* @ param int $line line - number to pass to query
* @ param string $file file - name to pass to query
* @ param string $app string with name of app , this need to be set in setup anyway !!!
* @ return ADORecordSet or false , if the query fails
*/
function insert ( $table , $data , $where , $line , $file , $app )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
if ( $this -> m_bDeltaOnly )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
// Don't run this query, since we are processing deltas only
return True ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
return $this -> m_odb -> insert ( $table , $data , $where , $line , $file , $app );
}
/**
* Execute the Sql statements in an array and give diagnostics , if any error occures
*
* @ param $aSql array of SQL strings to execute
* @ param $debug_level int for which debug_level ( and higher ) should the diagnostics always been printed
* @ param $debug string variable number of arguments for the debug_message functions in case of an error
* @ return int 2 : no error , 1 : errors , but continued , 0 : errors aborted
*/
function ExecuteSqlArray ( $aSql , $debug_level , $debug )
{
$retval = $this -> dict -> ExecuteSQLArray ( $aSql );
if ( $retval < 2 || $this -> debug >= $debug_level || $this -> debug > 3 )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$debug_params = func_get_args ();
array_shift ( $debug_params );
array_shift ( $debug_params );
call_user_method_array ( 'debug_message' , $this , $debug_params );
if ( $retval < 2 && ! $this -> dict -> debug )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
echo '<p><b>' . $this -> adodb -> ErrorMsg () . " </b></p> \n " ;
2002-03-03 22:48:34 +01:00
}
}
2004-08-13 21:01:03 +02:00
return $retval ;
}
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
/**
* Created a ( unique ) name for an index
*
* As the length of the index name is limited on some databases , we use two algorithms :
* a ) we use just the first column - name with and added _2 , _3 , ... if more indexes uses that column
* b ) we use the table - names plus all column - names and remove dublicate parts
*
* @ internal
* @ param $sTableName string name of the table
* @ param $aColumnNames array of column - names or string with a single column - name
* @ return string the index - name
*/
function _index_name ( $sTableName , $aColumnNames )
{
// this code creates extrem short index-names, eg. for MaxDB
if ( isset ( $this -> max_index_length [ $this -> sType ]) && $this -> max_index_length [ $this -> sType ] <= 32 )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
static $existing_indexes = array ();
if ( ! isset ( $existing_indexes [ $sTableName ]) && method_exists ( $this -> adodb , 'MetaIndexes' ))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$existing_indexes [ $sTableName ] = $this -> adodb -> MetaIndexes ( $sTableName );
}
$i = 0 ;
$firstCol = is_array ( $aColumnNames ) ? $aColumnNames [ 0 ] : $aColumnNames ;
do
2003-10-21 20:09:58 +02:00
{
2004-08-13 21:01:03 +02:00
++ $i ;
$name = $firstCol . ( $i > 1 ? '_' . $i : '' );
2003-10-21 20:09:58 +02:00
}
2004-08-13 21:01:03 +02:00
while ( isset ( $existing_indexes [ $sTableName ][ $name ]) || isset ( $existing_indexes [ strtoupper ( $sTableName )][ strtoupper ( $name )]));
$existing_indexes [ $sTableName ][ $name ] = True ; // mark it as existing now
return $name ;
}
// This code creates longer index-names incl. the table-names and the used columns
$table = str_replace ( array ( 'phpgw_' , 'egw_' ), '' , $sTableName );
// if the table-name or a part of it is repeated in the column-name, remove it there
$remove [] = $table . '_' ;
// also remove 3 or 4 letter shortcuts of the table- or app-name
$remove [] = substr ( $table , 0 , 3 ) . '_' ;
$remove [] = substr ( $table , 0 , 4 ) . '_' ;
// if the table-name consists of '_' limtied parts, remove occurences of these parts too
foreach ( explode ( '_' , $table ) as $part )
{
$remove [] = $part . '_' ;
}
$aColumnNames = str_replace ( $remove , '' , $aColumnNames );
$name = $sTableName . '_' . ( is_array ( $aColumnNames ) ? implode ( '_' , $aColumnNames ) : $aColumnNames );
return $name ;
}
/**
* Giving a non - fatal error - message
*/
function error ( $str )
{
echo " <p><b>Error:</b> $str </p> " ;
}
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
/**
* Giving a fatal error - message and exiting
*/
function fatal ( $str )
{
echo " <p><b>Fatal Error:</b> $str </p> " ;
exit ;
}
/**
* Gives out a debug - message with certain parameters
*
* All permanent debug - messages in the calendar should be done by this function !!!
* ( In future they may be logged or sent as xmlrpc - faults back . )
*
* Permanent debug - message need to make sure NOT to give secret information like passwords !!!
*
* This function do NOT honor the setting of the debug variable , you may use it like
* if ( $this -> debug > N ) $this -> debug_message ( 'Error ;-)' );
*
* The parameters get formated depending on their type .
*
* @ param $msg string message with parameters / variables like lang (), eg . '%1'
* @ param $backtrace include a function - backtrace , default True = On
* should only be set to False = Off , if your code ensures a call with backtrace = On was made before !!!
* @ param $param mixed a variable number of parameters , to be inserted in $msg
* arrays get serialized with print_r () !
*/
function debug_message ( $msg , $backtrace = True )
{
for ( $i = 2 ; $i < func_num_args (); ++ $i )
2003-10-21 20:09:58 +02:00
{
2004-08-13 21:01:03 +02:00
$param = func_get_arg ( $i );
if ( is_null ( $param ))
2003-12-17 14:58:02 +01:00
{
2004-08-13 21:01:03 +02:00
$param = 'NULL' ;
2003-12-17 14:58:02 +01:00
}
else
{
2004-08-13 21:01:03 +02:00
switch ( gettype ( $param ))
{
case 'string' :
$param = " ' $param ' " ;
break ;
case 'array' :
case 'object' :
list (, $content ) = @ each ( $param );
$do_pre = is_array ( $param ) ? count ( $param ) > 6 || is_array ( $content ) && count ( $content ) : True ;
$param = ( $do_pre ? '<pre>' : '' ) . print_r ( $param , True ) . ( $do_pre ? '</pre>' : '' );
break ;
case 'boolean' :
$param = $param ? 'True' : 'False' ;
break ;
}
2003-12-17 14:58:02 +01:00
}
2004-08-13 21:01:03 +02:00
$msg = str_replace ( '%' . ( $i - 1 ), $param , $msg );
2003-10-21 20:09:58 +02:00
}
2004-08-13 21:01:03 +02:00
echo '<p>' . $msg . " <br> \n " . ( $backtrace ? 'Backtrace: ' . function_backtrace ( 1 ) . " </p> \n " : '' );
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
/**
* Converts an eGW table - definition array into an ADOdb column - definition string
*
* @ internal
* @ param array $aTableDef eGW table - defintion
* @ return string ADOdb column - definition string ( comma separated )
*/
function _egw2adodb_columndef ( $aTableDef )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$ado_defs = array ();
foreach ( $aTableDef [ 'fd' ] as $col => $col_data )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$ado_col = False ;
2002-03-03 22:48:34 +01:00
2004-08-13 21:01:03 +02:00
switch ( $col_data [ 'type' ])
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
case 'auto' :
$ado_col = 'I AUTOINCREMENT NOTNULL' ;
unset ( $col_data [ 'nullable' ]); // else we set it twice
2002-03-03 22:48:34 +01:00
break ;
2004-08-13 21:01:03 +02:00
case 'blob' :
$ado_col = 'B' ;
2002-03-03 22:48:34 +01:00
break ;
2004-08-13 21:01:03 +02:00
case 'bool' :
$ado_col = 'L' ;
2002-03-03 22:48:34 +01:00
break ;
2004-08-13 21:01:03 +02:00
case 'char' :
// ADOdb does not differ between char and varchar
case 'varchar' :
if ( $col_data [ 'precision' ] > 0 && $col_data [ 'precision' ] < 256 )
{
$ado_col = " C( $col_data[precision] ) " ;
}
if ( $col_data [ 'precision' ] > 255 )
{
$ado_col = 'X' ;
}
2002-03-03 22:48:34 +01:00
break ;
2004-08-13 21:01:03 +02:00
case 'date' :
$ado_col = 'D' ;
if ( $col_data [ 'default' ] == 'current_date' )
{
$ado_col .= ' DEFDATE' ;
unset ( $col_data [ 'default' ]);
}
break ;
case 'decimal' :
$ado_col = " N( $col_data[precision] . $col_data[scale] ) " ;
break ;
2004-08-15 17:43:52 +02:00
case 'double' :
2004-08-13 21:01:03 +02:00
case 'float' :
// ADOdb does not differ between float and double
$ado_col = 'F' ;
break ;
case 'int' :
$ado_col = 'I' ;
switch ( $col_data [ 'precision' ])
{
case 1 :
case 2 :
case 4 :
case 8 :
$ado_col .= $col_data [ 'precision' ];
break ;
}
break ;
case 'longtext' :
$ado_col = 'XL' ;
break ;
case 'text' :
$ado_col = 'X' ;
break ;
case 'timestamp' :
$ado_col = 'T' ;
if ( $col_data [ 'default' ] == 'current_timestamp' )
{
$ado_col .= ' DEFTIMESTAMP' ;
unset ( $col_data [ 'default' ]);
}
2003-09-25 03:32:08 +02:00
break ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
if ( ! $ado_col )
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$this -> error ( " Ignoring unknown column-type ' $col_data[type] ( $col_data[precision] )' !!! " );
continue ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
if ( isset ( $col_data [ 'nullable' ]) && ! $col_data [ 'nullable' ])
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$ado_col .= ' NOTNULL' ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
if ( isset ( $col_data [ 'default' ]))
2002-03-03 22:48:34 +01:00
{
2004-08-13 21:01:03 +02:00
$ado_col .= " DEFAULT ' $col_data[default] ' " ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
if ( in_array ( $col , $aTableDef [ 'pk' ]))
{
$ado_col .= ' PRIMARY' ;
}
$ado_defs [] = $col . ' ' . $ado_col ;
2002-03-03 22:48:34 +01:00
}
2004-08-13 21:01:03 +02:00
//print_r($aTableDef); echo implode(",\n",$ado_defs)."\n";
return implode ( " , \n " , $ado_defs );
}
/**
2004-08-15 17:43:52 +02:00
* Read the table - definition direct from the database
*
* The definition might not be as accurate , depending on the DB !
*
* @ param string $sTableName table - name
* @ return array / boolean table - defition , like $phpgw_baseline [ $sTableName ] after including tables_current , or false on error
*/
function GetTableDefinition ( $sTableName )
{
if ( ! method_exists ( $this -> dict , 'MetaColumns' ) ||
! ( $columns = $this -> dict -> MetaColumns ( $sTableName )))
{
return False ;
}
$definition = array (
'fd' => array (),
'pk' => array (),
'fk' => array (),
'ix' => array (),
'uc' => array (),
);
foreach ( $columns as $column )
{
$name = strtolower ( $column -> name );
$type = method_exists ( $this -> dict , 'MetaType' ) ? $this -> dict -> MetaType ( $column ) : strtoupper ( $column -> type );
static $ado_type2egw = array (
'C' => 'varchar' ,
'C2' => 'varchar' ,
'X' => 'text' ,
'X2' => 'text' ,
'XL' => 'longtext' ,
'B' => 'blob' ,
'I' => 'int' ,
'T' => 'timestamp' ,
'D' => 'date' ,
'F' => 'float' ,
'N' => 'decimal' ,
);
$definition [ 'fd' ][ $name ][ 'type' ] = $ado_type2egw [ $type ];
switch ( $type )
{
case 'C' : case 'C2' :
$definition [ 'fd' ][ $name ][ 'type' ] = $column -> type == 'char' ? 'char' : 'varchar' ;
$definition [ 'fd' ][ $name ][ 'precision' ] = $column -> max_length ;
break ;
case 'F' :
$definition [ 'fd' ][ $name ][ 'precision' ] = $column -> max_length ;
break ;
case 'N' :
$definition [ 'fd' ][ $name ][ 'precision' ] = $column -> max_length ;
$definition [ 'fd' ][ $name ][ 'scale' ] = $column -> scale ;
break ;
case 'R' :
case 'I' : case 'I1' : case 'I2' : case 'I4' : case 'I8' :
if ( $column -> auto_increment )
{
$definition [ 'fd' ][ $name ] = array (
'type' => 'auto' ,
'nullable' => False ,
);
$column -> has_default = False ;
$definition [ 'pk' ][] = $name ;
}
else
{
$definition [ 'fd' ][ $name ][ 'type' ] = 'int' ;
}
switch ( $type )
{
case 'I1' : case 'I2' : case 'I4' : case 'I8' :
$definition [ 'fd' ][ $name ][ 'precision' ] = ( int ) $type [ 1 ];
break ;
default :
if ( $column -> max_length > 11 )
{
$definition [ 'fd' ][ $name ][ 'precision' ] = 8 ;
}
elseif ( $column -> max_length > 6 || ! $column -> max_length )
{
$definition [ 'fd' ][ $name ][ 'precision' ] = 4 ;
}
elseif ( $column -> max_length > 2 )
{
$definition [ 'fd' ][ $name ][ 'precision' ] = 2 ;
}
else
{
$definition [ 'fd' ][ $name ][ 'precision' ] = 1 ;
}
break ;
}
if ( $column -> has_default && is_null ( $colum -> default_value ))
{
$definition [ 'fd' ][ $name ][ 'default' ] = ( int ) $this -> default_value ;
$column -> has_default = false ;
}
break ;
}
if ( $column -> has_default )
{
$definition [ 'fd' ][ $name ][ 'default' ] = $column -> default_value ;
}
if ( $column -> not_null )
{
$definition [ 'fd' ][ $name ][ 'nullable' ] = False ;
}
if ( $column -> primary_key && ! in_array ( $name , $definition [ 'pk' ]))
{
$definition [ 'pk' ][] = $name ;
}
}
if ( method_exists ( $this -> dict , 'MetaIndexes' ) &&
count ( $indexes = $this -> dict -> MetaIndexes ( $sTableName )) > 0 )
{
foreach ( $indexes as $index )
{
if ( count ( $definition [ 'pk' ]) && ( implode ( ':' , $definition [ 'pk' ]) == implode ( ':' , $index [ 'columns' ]) ||
$index [ 'unique' ] && count ( array_intersect ( $definition [ 'pk' ], $index [ 'columns' ])) == count ( $definition [ 'pk' ])))
{
continue ; // is already the primary key => ignore it
}
$kind = $index [ 'unique' ] ? 'uc' : 'ix' ;
$definition [ $kind ][] = count ( $index [ 'columns' ]) > 1 ? $index [ 'columns' ] : $index [ 'columns' ][ 0 ];
}
}
if ( $this -> debug > 1 ) $this -> debug_message ( " schema_proc::GetTableDefintion('%1') = %2 " , False , $sTableName , $definition );
if ( $this -> debug > 2 ) $this -> debug_message ( " schema_proc::GetTableDefintion: MetaColumns('%1') = %2 " , False , $sTableName , $columns );
if ( $this -> debug > 2 ) $this -> debug_message ( " schema_proc::GetTableDefintion: MetaIndexes('%1') = %2 " , False , $sTableName , $indexes );
return $definition ;
}
/**
* Get actual columnnames as a comma - separated string in $sColumns and set table - definition in class - vars
*
* old translator function , use GetTableDefition () instead
2004-08-13 21:01:03 +02:00
* @ depricated
*/
function _GetColumns ( $oProc , $sTableName , & $sColumns )
{
2004-08-15 17:43:52 +02:00
$this -> sCol = $this -> pk = $this -> fk = $this -> ix = $this -> uc = array ();
$tabledef = $this -> GetTableDefinition ( $sTableName );
$sColumns = implode ( ',' , array_keys ( $tabledef [ 'fd' ]));
foreach ( $tabledef [ 'fd' ] as $column => $data )
{
$col_def = " 'type' => ' $data[type] ' " ;
unset ( $data [ 'type' ]);
foreach ( $data as $key => $val )
{
$col_def .= " , ' $key ' => " . ( is_bool ( $val ) ? ( $val ? 'true' : 'false' ) :
( is_int ( $val ) ? $val : " ' $val ' " ));
}
$this -> sCol [] = " \t \t \t \t ' $column ' => array( $col_def ), \n " ;
}
foreach ( array ( 'pk' , 'fk' , 'ix' , 'uc' ) as $kind )
{
$this -> $kind = $tabledef [ $kind ];
}
2002-03-03 22:48:34 +01:00
}
}
?>