Source for file BitDbBase.php
Documentation is available at BitDbBase.php
* ADOdb Library interface Class
* Copyright (c) 2004 bitweaver.org
* Copyright (c) 2003 tikwiki.org
* Copyright (c) 2002-2003, Luis Argerich, Garland Foster, Eduardo Polidor, et. al.
* All Rights Reserved. See below for details and a complete list of authors.
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See http://www.gnu.org/copyleft/lesser.html for details
* @author spider <spider@steelsun.com>
* ensure your AdoDB install is a subdirectory off your include path
define( 'BIT_QUERY_DEFAULT', - 1 ); // deprecated constant for no cache time
define( 'BIT_QUERY_CACHE_DISABLE', - 1 );
define( 'BIT_MAX_RECORDS', - 1 );
// num queries has to be global
* This class is used for database access and provides a number of functions to help
* with database portability.
* Currently used as a base class, this class should be optional to ensure bitweaver
* continues to function correctly, without a valid database connection.
* Used to store the ADODB db object used to access the database.
* This is just a pointer to a single global variable used by all classes.
* This limits database connections to just one per request.
* Used to identify the ADODB db object
* Used to store the ADODB db object type
* Used to store failed commands
* Used to store the number of queries executed.
* Used to store the total query time for this request.
* Case sensitivity flag used in convertQuery
* Used to enable AdoDB caching
* Used to determine SQL debug output. BitDbAdodb overrides associated methods to use the debugging mechanisms built into ADODB
* Determines if fatal query functions should terminate script execution. Defaults to TRUE. Can be deactived for things like expected duplicate inserts
* During initialisation, database parameters are passed to the class.
* If these parameters are not valid, class will not be initialised.
global $gBitDbCaseSensitivity;
* This function contains any pre-connection work
* @todo investigate if this is the correct way to do it.
if(isset ($this->mType)) {
// we have a db we're gonna try to load
// avoid database change messages
ini_set("sybct.min_server_severity", "11");
die("No database type specified");
* This function contains any post-connection work
* @todo investigate if this is the correct way to do it.
* @todo remove the BIT_DB_PREFIX, change to a member variable
* @todo get spiderr to explain the schema line
$this->mDb->Execute("set quoted_identifier on");
if( ($version['major'] >= 4 && $version['minor'] >= 1) || ($version['major'] >= 5) ) {
$this->mDb->Execute("set session sql_mode='PIPES_AS_CONCAT'");
// Do a little prep work for postgres, no break, cause we want default case too
// Assume we want to dump in a schema, so set the search path and nuke the prefix here.
// $result = $this->mDb->Execute( "SET search_path TO $schema,public" );
* Determines if the database connection is valid
* @return true if DB connection is valid, false if not
return( !empty( $this->mDb ) );
* Determines if the database connection is valid
* @return true if DB connection is valid, false if not
* Determines if the database connection is valid
* @return true if DB connection is valid, false if not
* Used to start query timer if in debug mode
$this->mQueryLap = $gBitTimer->elapsed();
/** will activate ADODB like native debugging output
* @param pLevel debugging level - FALSE is off, TRUE is on, 99 is verbose
function debug( $pLevel= 99 ) {
/** returns the level of query debugging output
* @return pLevel debugging level - FALSE is off, TRUE is on, 99 is verbose
* Sets the case sensitivity mode which is used in convertQuery
* @return true if DB connection is valid, false if not
* Sets the case sensitivity mode which is used in convertQuery
* @return true if DB connection is valid, false if not
// Force Oracle to always be insensitive
* Used to stop query tracking and output results if in debug mode
//count the number of queries made
$interval = $gBitTimer->elapsed() - $this->mQueryLap;
$style = ( $interval > .5 ) ? 'color:red;' : (( $interval > .15 ) ? 'color:orange;' : '');
$querySpeed = ( $interval > .5 ) ? tra( 'VERY SLOW' ): (( $interval > .15 ) ? tra( 'SLOW' ) : 'complete');
print '<p style="'. $style. '">
<span style="display:inline-block;width:30%">### Query: <strong>'. $gNumQueries. '</strong> '. $querySpeed. '</span>
<span style="display:inline-block;width:33%">Start time: '. round( $this->mQueryLap, 5 ). '</span>
<span style="display:inline-block;width:33%">### Query run time: '. round( $interval, 5 ). '</span></p>';
* Used to create tables - most commonly from package/schema_inc.php files
* @todo remove references to BIT_DB_PREFIX, us a member function
* @param pTables an array of tables and creation information in DataDict
* @param pOptions an array of options used while creating the tables
* true if created with no errors | false if errors are stored in $this->mFailed
* Used to check if tables already exists.
* @todo should be used to confirm tables are already created
* @param pTable the table name
* @return true if table already exists
* @todo remove references to BIT_DB_PREFIX, us a member function
* @param pTables an array of table names to drop
* true if dropped with no errors |
* false if errors are stored in $this->mFailed
* Function to set ADODB query caching member variable
* @param pCacheExecute flag to enable or disable ADODB query caching
* Function to set ADODB query caching member variable
* @param pCacheExecute flag to enable or disable ADODB query caching
* Quotes a string to be sent to the database
* @param pStr string to be quotes
* @return quoted string using AdoDB->qstr()
/** Queries the database, returning an error if one occurs, rather
* than exiting while printing the error. -rlpowell
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pError the error string to modify and return
* @param pValues an array of values used in a parameterised query
* @param pNumRows the number of rows (LIMIT) to return in this query
* @param pOffset the row number to begin returning rows from. Used in
* @return an AdoDB RecordSet object
* conjunction with $pNumRows
* @todo currently not used anywhere.
function queryError( $pQuery, &$pError, $pValues = NULL, $pNumRows = - 1, $pOffset = - 1 ) {
/** Queries the database reporting an error if detected
* than exiting while printing the error. -rlpowell
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @param pNumRows the number of rows (LIMIT) to return in this query
* @param pOffset the row number to begin returning rows from. Used in
* conjunction with $pNumRows
* @return an AdoDB RecordSet object
function query($query, $values = null, $numrows = BIT_QUERY_DEFAULT, $offset = BIT_QUERY_DEFAULT, $pCacheTime= BIT_QUERY_DEFAULT ) {
* ADODB compatibility functions for bitcommerce
function Execute($pQuery, $pNumRows = false, $zf_cache = false, $pCacheTime= BIT_QUERY_DEFAULT) {
if ( $this->mType == "firebird") {
return $this->query( $pQuery, NULL, $pNumRows, NULL, $pCacheTime );
* Create a list of tables available in the current database
* @param ttype can either be 'VIEW' or 'TABLE' or false.
* If false, both views and tables are returned.
* "VIEW" returns only views
* "TABLE" returns only tables
* @param showSchema returns the schema/user with the table name, eg. USER.TABLE
* @param mask is the input mask - only supported by oci8 and postgresql
* @return array of tables for current database.
function MetaTables( $ttype = false, $showSchema = false, $mask= false ) {
* List columns in a database as an array of ADOFieldObjects.
* See top of file for definition of object.
* @param table table name to query
* @param upper uppercase table name (required by some databases)
* @param schema is optional database schema to use - not supported by all databases.
* @return array of ADOFieldObjects for current table.
function MetaColumns($table,$normalize= true, $schema= false) {
* List indexes in a database as an array of ADOFieldObjects.
* See top of file for definition of object.
* @param table table name to query
* @param primary list primary indexes
* @param owner list owner of index
* @return array of ADOFieldObjects for current table.
function MetaIndexes($table,$primary= false, $owner= false) {
/** Executes the SQL and returns all elements of the first column as a 1-dimensional array. The recordset is discarded for you automatically. If an error occurs, false is returned.
* See AdoDB GetCol() function for more detail.
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @param pForceArray if set to true, when an array is created for each value
* @param pFirst2Cols if set to true, only returns the first two columns
* @return the associative array, or false if an error occurs
* @todo not currently used anywhere
function getCol( $pQuery, $pValues= FALSE, $pTrim= FALSE ) {
/** Returns an associative array for the given query.
* See AdoDB GetAssoc() function for more detail.
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @param pForceArray if set to true, when an array is created for each value
* @param pFirst2Cols if set to true, only returns the first two columns
* @return the associative array, or false if an error occurs
function getArray( $pQuery, $pValues= FALSE, $pForceArray= FALSE, $pFirst2Cols= FALSE, $pCacheTime= BIT_QUERY_DEFAULT ) {
/** Returns an associative array for the given query.
* See AdoDB GetAssoc() function for more detail.
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @param pForceArray if set to true, when an array is created for each value
* @param pFirst2Cols if set to true, only returns the first two columns
* @return the associative array, or false if an error occurs
function getAssoc( $pQuery, $pValues= FALSE, $pForceArray= FALSE, $pFirst2Cols= FALSE, $pCacheTime= BIT_QUERY_DEFAULT ) {
/** Executes the SQL and returns the first row as an array. The recordset and remaining rows are discarded for you automatically. If an error occurs, false is returned.
* See AdoDB GetRow() function for more detail.
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @return returns the first row as an array, or false if an error occurs
function getRow( $pQuery, $pValues= FALSE, $pCacheTime= BIT_QUERY_DEFAULT ) {
/** Returns a single column value from the database.
* @param pQuery the SQL query. Use backticks (`) to quote all table
* and attribute names for AdoDB to quote appropriately.
* @param pValues an array of values used in a parameterised query
* @param pReportErrors report errors to STDOUT
* @param pOffset the row number to begin returning rows from.
* @return the associative array, or false if an error occurs
function getOne($pQuery, $pValues= NULL, $pNumRows= NULL, $pOffset= NULL, $pCacheTime = BIT_QUERY_DEFAULT ) {
* This function will take a set of fields identified by an associative array - $insertData
* generate a suitable SQL script
* and insert the data into the specified table - $insertTable
* @param insertTable Name of the table to be inserted into
* @param insertData Array of data to be inserted. Array keys provide the field names
* @return Error status of the insert
//stupid little loop to generate question marks. Start at one, and tack at the end to ease dealing with comma
for( $i = 1; $i < count( $insertData ); $i++ ) {
if( $insertTable[0] != '`' ) {
$insertTable = '`'. $insertTable. '`';
$query = "INSERT INTO $insertTable ( $setSql ) VALUES ( $valueSql )";
* This function will take a set of fields identified by an associative array - $updateData
* generate a suitable SQL script
* update the data into the specified table
* at the location identified in updateId which holds a name and value entry
* @param updateTable Name of the table to be updated
* @param updateData Array of data to be changed. Array keys provide the field names
* If an array key contains an '=' it will assumed to already be properly quoted.
* This allows use of keys like this: `column_name` = `column_name` + ?
* @param updateId Array identifying the record to update.
* Array key 'name' provide the field name, and 'value' the record key
* @return Error status of the insert
foreach( $updateData as $key=> $value ) {
if (strpos($key,'=') === false) {
$setSql .= ", `$key` = ?";
if( $updateTable[0] != '`' ) {
$updateTable = '`'. $updateTable. '`';
$query = "UPDATE $updateTable SET $setSql WHERE $keyNames";
$result = $this->query( $query, $bindVars );
* A database portable Sequence management function.
* @param pSequenceName Name of the sequence to be used
* It will be created if it does not already exist
* @return 0 if not supported, otherwise a sequence id
function GenID( $pSequenceName, $pUseDbPrefix = true ) {
* A database portable Sequence management function.
* @param pSequenceName Name of the sequence to be used
* It will be created if it does not already exist
* @param pStartID Allows setting the initial value of the sequence
* @return 0 if not supported, otherwise a sequence id
* @todo To be combined with GenID
* A database portable IFNULL function.
* @param pField argument to compare to NULL
* @param pNullRepl the NULL replacement value
* @return a string that represents the function that checks whether
* $pField is NULL for the given database, and if NULL, change the
* value returned to $pNullRepl.
function ifNull($pField, $pNullRepl) {
* A database portable RANDOM() function.
* Adodb overrides it anyway with it's $rand property.
* @return string with RANDOM() function.
/** Format the timestamp in the format the database accepts.
* @param pDate a Unix integer timestamp or an ISO format Y-m-d H:i:s
* @return the timestamp as a quoted string.
* @todo could be used to later convert all int timestamps into db
* timestamps. Currently not used anywhere.
* Return the current timestamp literal relevent to the database type
* @todo This needs extending to allow the use of GMT timestamp
* rather then the current server time
global $gBitDbType, $gBitSystem;
$ret = $gBitSystem->getUTCTimestamp(); // UTC time to get round server offsets
* Return the current timestamp literal relevent to the database type
* @todo This needs extending to allow the use of GMT timestamp
* rather then the current server time
global $gBitDbType, $gBitSystem;
$ret = "'". $gBitSystem->getUTCTimestamp(). "'"; // UTC time to get round server offsets
/** Return the sql to cast the given column from a time stamp to a Unix epoch
* this is most useful for the many places bitweaver stores time as epoch integers
* ADODB has no native support for this, see http://phplens.com/lens/lensforum/msgs.php?id=13661&x=1
* @param pColumn name of an integer, or long integer column
* @return the timestamp as a quoted string.
* @todo could be used to later convert all int timestamps into db
* timestamps. Currently not used anywhere.
$ret = "CAST `$pColumn` AS TIMESTAMP";
$ret = "UNIX_TIMESTAMP( `$pColumn` )";
$ret = $pColumn. '::abstime::integer';
/** Return the sql to cast the given column from an long integer to a time stamp.
* this is most useful for the many places bitweaver stores time as epoch integers
* ADODB has no native support for this, see http://phplens.com/lens/lensforum/msgs.php?id=13661&x=1
* @param pColumn name of an integer, or long integer column
* @return the timestamp as a quoted string.
* @todo could be used to later convert all int timestamps into db
* timestamps. Currently not used anywhere.
$ret = "(`$pColumn` / 86400.000000) + CAST ( '01/01/1970' AS TIMESTAMP )";
$ret = "CAST( `$pColumn` AS DATETIME )";
$ret = $pColumn. '::integer::abstime::timestamptz';
/** Return the sql to lock selected rows for updating.
* ADODB has no native support for this, see http://phplens.com/lens/lensforum/msgs.php?id=13661&x=1
* @param pColumn name of an integer, or long integer column
* @return the timestamp as a quoted string.
* @todo could be used to later convert all int timestamps into db
* timestamps. Currently not used anywhere.
* Format date column in sql string given an input format that understands Y M D
function SQLDate($pDateFormat, $pBaseDate= false) {
* Calculate the offset of a date for a particular database and generate
* appropriate SQL. Useful for calculating future/past dates and storing
* @param pDays Number of days to offset by
* If dayFraction=1.5 means 1.5 days from now, 1.0/24 for 1 hour.
* @param pColumn Value to be offset
* If NULL an offset from the current time is supplied
* @return New number of days
* @todo Not currently used - this is database specific and uses TIMESTAMP
* rather than unix seconds
/** Converts backtick (`) quotes to the appropriate quote for the
* @param pQuery the SQL query using backticks (`)
* @return the correctly quoted SQL statement
* @todo investigate replacement by AdoDB NameQuote() function
if( !empty( $this->mType ) ) {
// Force Oracle to always be insensitive
case "postgres": // For PEAR
case "postgres7": // Deprecated ADODB
* Converts field sorting abbreviation to SQL - you can pass in a single string or an entire array of sortmodes
* @param string or array $pSortMode fieldname and sort order string (eg name_asc)
* @return the correctly quoted SQL ORDER statement
foreach( $pSortMode as $sortMode ) {
* Converts field sorting abbreviation to SQL and it also allows us to do things like sort by random rows.
* @param array $pSortMode If pSortMode is 'random' it will insert the properly named db-specific function to achieve this.
* @return valid, database-specific sortmode - if sortmode is not valid, NULL is returned
// check $sort_mode for evil stuff
if( $pSortMode = preg_replace('/[^.0-9A-Za-z_,]/', '', $pSortMode) ) {
if( $sep = strrpos( $pSortMode, '_' ) ) {
$order = substr( $pSortMode, $sep );
// force ending to neither _asc or _desc
if ( $order != '_asc' && $order != '_desc' ) {
$pSortMode = substr( $pSortMode, 0, $sep ) . '_desc';
} elseif( $pSortMode != 'random' ) {
$pSortMode = preg_replace( '/lastModif/', 'last_modified', $pSortMode );
$pSortMode = preg_replace( '/pageName/', 'title', $pSortMode );
$pSortMode = preg_replace( '/^user_(asc|desc)/', 'login_\1', $pSortMode );
//Use random() of BitDbBase. BitDbAdodb will override it with its implementation.
if( $pSortMode == "random" ) {
$pSortMode = $this->random ();
$pSortMode = preg_replace( "/_asc$/", "` ASC NULLS LAST", $pSortMode );
$pSortMode = preg_replace( "/_desc$/", "` DESC NULLS LAST", $pSortMode );
// Use of alias in order by is not supported because of optimizer processing
if ( $pSortMode == 'page_name_asc' ) $pSortMode = 'title_asc';
if ( $pSortMode == 'page_name_desc' ) $pSortMode = 'title_desc';
if ( $pSortMode == 'content_id_asc' ) $pSortMode = 'lc.content_id_asc';
if ( $pSortMode == 'content_id_desc' ) $pSortMode = 'lc.content_id_desc';
if ( $pSortMode == 'item_position_asc' ) $pSortMode = 'tfgim2.item_position_asc';
if ( $pSortMode == 'item_position_desc' ) $pSortMode = 'tfgim2.item_position_desc';
if ( $pSortMode == 'creator_user_asc' ) $pSortMode = 'uuc.login_asc';
if ( $pSortMode == 'creator_user_desc' ) $pSortMode = 'uuc.login_desc';
if ( $pSortMode == 'creator_real_name_asc' ) $pSortMode = 'uuc.real_name_asc';
if ( $pSortMode == 'creator_real_name_desc' ) $pSortMode = 'uuc.real_name_desc';
if ( $pSortMode == 'modifier_user_asc' ) $pSortMode = 'uue.login_asc';
if ( $pSortMode == 'modifier_user_desc' ) $pSortMode = 'uue.login_desc';
if ( $pSortMode == 'modifier_real_name_asc' ) $pSortMode = 'uue.real_name_asc';
if ( $pSortMode == 'modifier_real_name_desc' ) $pSortMode = 'uue.real_name_desc';
$pSortMode = preg_replace( "/_asc$/", "` ASC", $pSortMode );
$pSortMode = preg_replace( "/_desc$/", "` DESC", $pSortMode );
if( strpos( $pSortMode, '.' ) ) {
$pSortMode = "`" . $pSortMode;
/** Returns the keyword to force a column comparison to be case sensitive
* for none case-sensitive databases (eg MySQL)
* @return the SQL keyword
* @todo only used in gBitSystem and users_lib to compare login names
/** Used to cast variable types for certain databases (ie SyBase & MSSQL)
* @param pVar the variable value to cast
* @param pType the current variable type
* @return the SQL casting statement
return " CONVERT(numeric(14,0),$pVar) ";
return " CONVERT(varchar(255),$pVar) ";
return " CONVERT(numeric(10,5),$pVar) ";
* Used to encode blob data (eg PostgreSQL). Can be called statically
* @todo had a lot of trouble with AdoDB BlobEncode and BlobDecode
* the code works but will need work for dbs other than PgSQL
* @param pData a string of raw blob data
* @return escaped blob data
// need to use this global so as not to break static calls
$replace = array('\\\134', '\\\000', '\\\047');
* Used to decode blob data (eg PostgreSQL)
* @todo had a lot of trouble with AdoDB BlobEncode and BlobDecode
* the code works but will need work for dbs other than PgSQL
* @param pData escaped blob data
* @return a string of raw blob data
switch ($this->mDb->mType) {
* Improved method of initiating a transaction. Used together with CompleteTrans().
* a. StartTrans/CompleteTrans is nestable, unlike BeginTrans/CommitTrans/RollbackTrans.
* Only the outermost block is treated as a transaction.<br>
* b. CompleteTrans auto-detects SQL errors, and will rollback on errors, commit otherwise.<br>
* c. All BeginTrans/CommitTrans/RollbackTrans inside a StartTrans/CompleteTrans block
* are disabled, making it backward compatible.
* Used together with StartTrans() to end a transaction. Monitors connection
* for sql errors, and will commit or rollback as appropriate.
* autoComplete if true, monitor sql errors and commit and rollback as appropriate,
* and if set to false force rollback even if no SQL error detected.
* @returns true on commit, false on rollback.
* If database does not support transactions, rollbacks always fail, so return false
* otherwise returns true if the Rollback was successful
* @return # rows affected by UPDATE/DELETE
* Check for Postgres specific extensions
// This code makes use of the badass /usr/share/pgsql/contrib/tablefunc.sql
// contribution that you have to install like: psql foo < /usr/share/pgsql/contrib/tablefunc.sql
return defined( 'ADVANCED_PGSQL' );
* determine current version of the databse
* @return # hash including 'description', 'version' full string, 'major', 'minor', and 'revsion'
$ret = $this->mDb->ServerInfo();
$versionHash = explode( '.', $ret['version'] );
$ret['major'] = !empty( $versionHash[0] ) ? $versionHash[0] : 0;
$ret['minor'] = !empty( $versionHash[1] ) ? $versionHash[1] : 0;
$ret['revision'] = !empty( $versionHash[2] ) ? $versionHash[2] : 0;
* Compatibility function for DBs with case insensitive searches
* (like MySQL, see: http://dev.mysql.com/doc/refman/5.1/en/case-sensitivity.html)
* AND ".$this->mDb->getCaseLessColumn('lc.title')." = 'page title'
* The reason all this matters is that huge performane difference between:
* where title = 'PAGE TITLE'
* where UPPER(tittle) = 'PAGE TITTLE'
* The latter version will not make use of the index on page title (at least for MySQl)
* while the first vesion will use the index. In a case insensitive search DB (MySQL) both
* forms of the query will give the same results, the only difference being the preformance.
* Spiderr suggested this solution and suppled the code below
$ret = " UPPER($pColumn) ";
* Renamed a few functions - these are the temporary backward compatability calls with the deprecated note
* These funcitons will be removed in due course
* @deprecated deprecated since version 2.0.0
* @deprecated deprecated since version 2.0.0
deprecated( $this->depText( 'convert_sortmode_one_item', 'convertSortmodeOneItem' ) );
* @deprecated deprecated since version 2.0.0
* @deprecated deprecated since version 2.0.0
return $this->sqlCast( $pVar, $pType );
* @deprecated deprecated since version 2.0.0
* @deprecated deprecated since version 2.0.0
return "We have changed this method to BitDbBase::{$pTo}().
Please update your code accordingly - you can try using the following (please back up your code before applying this):
find <your package>/ -name \"*.php\" -exec perl -i -wpe 's/\b{$pFrom}\b/{$pTo}/g' {} \;";
|