loganalyzer/src/classes/logstreamdb.class.php
2010-01-14 09:54:34 +01:00

1422 lines
40 KiB
PHP

<?php
/*
*********************************************************************
* -> www.phplogcon.org <- *
* ----------------------------------------------------------------- *
* Some constants *
* *
* LogStreamDB provides access to the data in database. In the most
* cases this will be plain text files. If we need access to e.g.
* zipped files, this will be handled by a separate driver.
*
* \version 2.0.0 Init Version
* *
* All directives are explained within this file *
*
* Copyright (C) 2008 Adiscon GmbH.
*
* This file is part of phpLogCon.
*
* PhpLogCon 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 3 of the License, or
* (at your option) any later version.
*
* PhpLogCon is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with phpLogCon. If not, see <http://www.gnu.org/licenses/>.
*
* A copy of the GPL can be found in the file "COPYING" in this
* distribution.
*********************************************************************
*/
// --- Avoid directly accessing this file!
if ( !defined('IN_PHPLOGCON') )
{
die('Hacking attempt');
exit;
}
// ---
// --- Required Includes!
require_once($gl_root_path . 'include/constants_errors.php');
// ---
class LogStreamDB extends LogStream {
private $_dbhandle = null;
// Helper to store the database records
private $bufferedRecords = null;
private $_currentRecordStart = 0;
private $_currentRecordNum = 0;
private $_totalRecordCount = -1;
private $_previousPageUID = -1;
private $_lastPageUID = -1;
private $_firstPageUID = -1;
private $_currentPageNumber = 0;
private $_SQLwhereClause = "";
private $_myDBQuery = null;
// Constructor
public function LogStreamDB($streamConfigObj) {
$this->_logStreamConfigObj = $streamConfigObj;
if ( $this->_logStreamConfigObj->DBType == DB_MYSQL )
{
// Probe if a function exists!
if ( !function_exists("mysql_connect") )
DieWithFriendlyErrorMsg("Error, MYSQL Extensions are not enabled! Function 'mysql_connect' does not exist.");
}
}
/**
* Open and verifies the database conncetion
*
* @param arrProperties array in: Properties wish list.
* @return integer Error stat
*/
public function Open($arrProperties)
{
global $dbmapping;
// Initialise Basic stuff within the Classs
$this->RunBasicInits();
// Verify database connection (This also opens the database!)
$res = $this->Verify();
if ( $res != SUCCESS )
return $res;
// Copy the Property Array
$this->_arrProperties = $arrProperties;
// Check if DB Mapping exists
if ( !isset($dbmapping[ $this->_logStreamConfigObj->DBTableType ]) )
return ERROR_DB_INVALIDDBMAPPING;
// Create SQL Where Clause first!
$res = $this->CreateSQLWhereClause();
if ( $res != SUCCESS )
return $res;
// Success, this means we init the Pagenumber to ONE!
$this->_currentPageNumber = 1;
// reached this point means success!
return SUCCESS;
}
/**
* Close the database connection.
*
* @return integer Error state
*/
public function Close()
{
if ($this->_dbhandle)
mysql_close($this->_dbhandle);
$this->_dbhandle = null;
return SUCCESS;
}
/**
* Verify if the database connection exists!
*
* @return integer Error state
*/
public function Verify() {
// Try to connect to the database
if ( $this->_dbhandle == null )
{
// Forces to open a new link in all cases!
$this->_dbhandle = @mysql_connect($this->_logStreamConfigObj->DBServer,$this->_logStreamConfigObj->DBUser,$this->_logStreamConfigObj->DBPassword, true);
if (!$this->_dbhandle)
{
if ( isset($php_errormsg) )
{
global $extraErrorDescription;
$extraErrorDescription = $php_errormsg;
}
// Return error code
return ERROR_DB_CONNECTFAILED;
}
}
// Select the database now!
$bRet = @mysql_select_db($this->_logStreamConfigObj->DBName, $this->_dbhandle);
if(!$bRet)
{
if ( isset($php_errormsg) )
{
global $extraErrorDescription;
$extraErrorDescription = $php_errormsg;
}
// Return error code
return ERROR_DB_CANNOTSELECTDB;
}
// Check if the table exists!
$numTables = @mysql_num_rows( mysql_query("SHOW TABLES LIKE '%" . $this->_logStreamConfigObj->DBTableName . "%'"));
if( $numTables <= 0 )
return ERROR_DB_TABLENOTFOUND;
// reached this point means success ;)!
return SUCCESS;
}
/**
* Read the data from a specific uID which means in this
* case beginning with from the Database ID
*
* @param uID integer in/out: unique id of the data row
* @param arrProperitesOut array out: array filled with properties
* @return integer Error state
* @see ReadNext()
*/
public function Read($uID, &$arrProperitesOut)
{
// Seek the first uID!
if ( $this->Sseek($uID, EnumSeek::UID, 0) == SUCCESS)
{
// Read the next record!
$ret = $this->ReadNext($uID, $arrProperitesOut);
}
else
$ret = ERROR_NOMORERECORDS;
// return result!
return $ret;
}
/**
* Read the next line from the file depending on the current
* read direction.
*
* Hint: If the current stream becomes unavailable an error
* stated is retuned. A typical case is if a log rotation
* changed the original data source.
*
* @param uID integer out: uID is the offset of data row
* @param arrProperitesOut array out: properties
* @return integer Error state
* @see ReadNext
*/
public function ReadNext(&$uID, &$arrProperitesOut, $bParseMessage = true)
{
// Helpers needed for DB Mapping
global $content, $gl_starttime;
global $dbmapping, $fields;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// define $ret
$ret = SUCCESS;
do
{
// No buffer? then read from DB!
if ( $this->bufferedRecords == null )
$ret = $this->ReadNextRecordsFromDB($uID);
else
{
if ( !isset($this->bufferedRecords[$this->_currentRecordNum] ) )
{
// We need to load new records, so clear the old ones first!
$this->ResetBufferedRecords();
// Set new Record start, will be used in the SQL Statement!
$this->_currentRecordStart = $this->_currentRecordNum; // + 1;
// Now read new ones
$ret = $this->ReadNextRecordsFromDB($uID);
// Check if we found more records
if ( !isset($this->bufferedRecords[$this->_currentRecordNum] ) )
$ret = ERROR_NOMORERECORDS;
}
}
if ( $ret == SUCCESS && $this->_arrProperties != null )
{
// Init and set variables
foreach ( $this->_arrProperties as $property )
{
// Check if mapping exists
if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$property]) )
{
// Copy property if available!
$dbfieldname = $dbmapping[$szTableType]['DBMAPPINGS'][$property];
if ( isset($this->bufferedRecords[$this->_currentRecordNum][$dbfieldname]) )
{
if ( isset($fields[$property]['FieldType']) && $fields[$property]['FieldType'] == FILTER_TYPE_DATE ) // Handle as date!
$arrProperitesOut[$property] = GetEventTime( $this->bufferedRecords[$this->_currentRecordNum][$dbfieldname] );
else
$arrProperitesOut[$property] = $this->bufferedRecords[$this->_currentRecordNum][$dbfieldname];
}
else
$arrProperitesOut[$property] = '';
}
else
$arrProperitesOut[$property] = '';
}
// Run optional Message Parsers now
if ( isset($arrProperitesOut[SYSLOG_MESSAGE]) )
{
$retParser = $this->_logStreamConfigObj->ProcessMsgParsers($arrProperitesOut[SYSLOG_MESSAGE], $arrProperitesOut);
// Check if we have to skip the message!
if ( $retParser == ERROR_MSG_SKIPMESSAGE )
$ret = $retParser;
}
// Set uID to the PropertiesOut! //DEBUG -> $this->_currentRecordNum;
$uID = $arrProperitesOut[SYSLOG_UID] = $this->bufferedRecords[$this->_currentRecordNum][$dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID]];
// Increment $_currentRecordNum
$this->_currentRecordNum++;
}
// Check how long we are running. If only two seconds of execution time are left, we abort further reading!
$scriptruntime = intval(microtime_float() - $gl_starttime);
if ( $content['MaxExecutionTime'] > 0 && $scriptruntime > ($content['MaxExecutionTime']-2) )
{
// This may display a warning message, so the user knows we stopped reading records because of the script timeout.
$content['logstream_warning'] = "false";
$content['logstream_warning_details'] = $content['LN_WARNING_LOGSTREAMDISK_TIMEOUT'];
$content['logstream_warning_code'] = ERROR_FILE_NOMORETIME;
// Return error code
return ERROR_FILE_NOMORETIME;
}
// This additional filter check will take care on dynamic fields from the message parser!
} while ( $this->ApplyFilters($ret, $arrProperitesOut) != SUCCESS && $ret == SUCCESS );
// reached here means return result!
return $ret;
}
/**
* Implementation of Seek
*/
public function Sseek(&$uID, $mode, $numrecs)
{
// predefine return value
$ret = SUCCESS;
switch ($mode)
{
case EnumSeek::UID:
// if ( $uID == UID_UNKNOWN ) // set uID to first ID!
{
// No buffer? then read from DB!
if ( $this->bufferedRecords == null )
$ret = $this->ReadNextRecordsFromDB($uID);
if ( $ret == SUCCESS )
{
$this->_currentRecordNum = 0;
$uID = $this->bufferedRecords[ $this->_currentRecordNum ];
}
}
break;
}
// Return result!
return $ret;
}
/**
* GetMessageCount will return the count of Message.
* If this count is not available, the function will
* return the default -1
*/
public function GetMessageCount()
{
return $this->_totalRecordCount;
}
/**
* This function returns the first UID for previous PAGE, if availbale!
* Otherwise will return -1!
*/
public function GetPreviousPageUID()
{
return $this->_previousPageUID;
}
/**
* This function returns the FIRST UID for the FIRST PAGE!
* Will be done by a seperated SQL Statement.
*/
public function GetFirstPageUID()
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Only perform query if row counting is enabled!
if ( strlen($this->_SQLwhereClause) > 0 && !$this->_logStreamConfigObj->DBEnableRowCounting )
return $this->_firstPageUID;
$szSql = "SELECT MAX(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") FROM " . $this->_logStreamConfigObj->DBTableName . $this->_SQLwhereClause;
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// obtain first and only row
$myRow = mysql_fetch_row($myQuery);
$this->_firstPageUID = $myRow[0];
// Free query now
mysql_free_result ($myQuery);
// Increment for the Footer Stats
$querycount++;
}
// Return result!
return $this->_firstPageUID;
}
/**
* This function returns the first UID for the last PAGE!
* Will be done by a seperated SQL Statement.
*/
public function GetLastPageUID()
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Only perform query if row counting is enabled!
if ( strlen($this->_SQLwhereClause) > 0 && !$this->_logStreamConfigObj->DBEnableRowCounting )
return $this->_lastPageUID;
$szSql = "SELECT MIN(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") FROM " . $this->_logStreamConfigObj->DBTableName . $this->_SQLwhereClause;
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// obtain first and only row
$myRow = mysql_fetch_row($myQuery);
$this->_lastPageUID = $myRow[0];
// Free query now
mysql_free_result ($myQuery);
// Increment for the Footer Stats
$querycount++;
}
// Return result!
return $this->_lastPageUID;
}
/**
* This function returns the current Page number, if availbale!
* Otherwise will return 0! We also assume that this function is
* only called once DB is open!
*/
public function GetCurrentPageNumber()
{
return $this->_currentPageNumber;
}
/*
* Implementation of IsPropertySortable
*
* For now, sorting is only possible for the UID Property!
*/
public function IsPropertySortable($myProperty)
{
global $fields;
// TODO: HARDCODED | FOR NOW only FALSE!
return false;
if ( isset($fields[$myProperty]) && $myProperty == SYSLOG_UID )
return true;
else
return false;
}
/**
* Implementation of GetLogStreamStats
*
* Returns an Array og logstream statsdata
* Count of Data Items
* Total Filesize
*/
public function GetLogStreamStats()
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Perform if Connection is true!
if ( $this->_dbhandle != null )
{
// Obtain Stats data for this table!
$szSql = "SHOW TABLE STATUS FROM " . $this->_logStreamConfigObj->DBName;
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// Loop through results
while ($myRow = mysql_fetch_array($myQuery, MYSQL_ASSOC))
{
// Set tablename!
$tableName = $myRow['Name'];
$myStats = null;
$myStats[] = array( 'StatsDisplayName' => 'Table name', 'StatsValue' => $tableName );
// copy usefull statsdata
if ( isset($myRow['Engine']) )
$myStats[] = array( 'StatsDisplayName' => 'Table engine', 'StatsValue' => $myRow['Engine'] );
if ( isset($myRow['Rows']) )
$myStats[] = array( 'StatsDisplayName' => 'Rowcount', 'StatsValue' => $myRow['Rows'] );
if ( isset($myRow['Data_length']) )
$myStats[] = array( 'StatsDisplayName' => 'Table filesize (bytes)', 'StatsValue' => $myRow['Data_length'] );
if ( isset($myRow['Collation']) )
$myStats[] = array( 'StatsDisplayName' => 'Collation', 'StatsValue' => $myRow['Collation'] );
if ( isset($myRow['Comment']) )
$myStats[] = array( 'StatsDisplayName' => 'Comment', 'StatsValue' => $myRow['Comment'] );
$stats[]['STATSDATA'] = $myStats;
}
// Free query now
mysql_free_result ($myQuery);
// Increment for the Footer Stats
$querycount++;
}
// return results!
return $stats;
}
else
return null;
}
/**
* Implementation of GetLogStreamTotalRowCount
*
* Returns the total amount of rows in the main datatable
*/
public function GetLogStreamTotalRowCount()
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Set default rowcount
$rowcount = null;
// Perform if Connection is true!
if ( $this->_dbhandle != null )
{
// SHOW TABLE STATUS FROM
$szSql = "SELECT count(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") as Counter FROM " . $this->_logStreamConfigObj->DBTableName;
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// Obtain RowCount!
$myRow = mysql_fetch_row($myQuery);
$rowcount = $myRow[0];
// Free query now
mysql_free_result ($myQuery);
// Increment for the Footer Stats
$querycount++;
}
}
//return result
return $rowcount;
}
/**
* Implementation of the CleanupLogdataByDate function! Returns affected rows!
*/
public function CleanupLogdataByDate( $nDateTimeStamp )
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Set default rowcount
$rowcount = null;
// Perform if Connection is true!
if ( $this->_dbhandle != null )
{
// Create WHERE attachment
if ( $nDateTimeStamp > 0 )
$szWhere = " WHERE UNIX_TIMESTAMP(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_DATE] . ") < " . $nDateTimeStamp;
else
$szWhere = "";
// DELETE DATA NOW!
$szSql = "DELETE FROM " . $this->_logStreamConfigObj->DBTableName . $szWhere;
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// Get affected rows and return!
$rowcount = mysql_affected_rows();
// Free result not needed here!
//mysql_free_result ($myQuery);
}
else
{
// error occured, output DEBUG message
$this->PrintDebugError("CleanupLogdataByDate failed with SQL Statement ' " . $szSql . " '");
}
}
//return affected rows
return $rowcount;
}
/*
* Implementation of the SaveMessageChecksum
*
* Creates an database UPDATE Statement and performs it!
*/
public function SaveMessageChecksum( $arrProperitesIn )
{
global $querycount, $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
if ( isset($arrProperitesIn[SYSLOG_UID]) && isset($arrProperitesIn[MISC_CHECKSUM]) && isset($dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM]) )
{
// DELETE DATA NOW!
$szSql = "UPDATE " . $this->_logStreamConfigObj->DBTableName .
" SET " . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . " = " . $arrProperitesIn[MISC_CHECKSUM] .
" WHERE " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " = " . $arrProperitesIn[SYSLOG_UID];
$myQuery = mysql_query($szSql, $this->_dbhandle);
if ($myQuery)
{
// Return success
return SUCCESS;
}
else
{
// error occured, output DEBUG message
$this->PrintDebugError("SaveMessageChecksum failed with SQL Statement ' " . $szSql . " '");
// Failed
return ERROR;
}
}
else
// Missing important properties
return ERROR;
}
/**
* Implementation of ConsolidateItemListByField
*
* In the native MYSQL Logstream, the database will do most of the work
*
* @return integer Error stat
*/
public function ConsolidateItemListByField($szConsFieldId, $nRecordLimit, $szSortFieldId, $nSortingOrder)
{
global $content, $dbmapping, $fields;
// Copy helper variables, this is just for better readability
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Check if fields are available
if ( !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId]) || !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szSortFieldId]) )
return ERROR_DB_DBFIELDNOTFOUND;
// --- Set Options
$nConsFieldType = $fields[$szConsFieldId]['FieldType'];
if ( $nSortingOrder == SORTING_ORDER_DESC )
$szSortingOrder = "DESC";
else
$szSortingOrder = "ASC";
// ---
// --- Set DB Field names
$myDBConsFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId];
$myDBGroupByFieldName = $myDBConsFieldName;
$myDBQueryFields = $myDBConsFieldName . ", ";
// Set Sorted Field
if ( $szConsFieldId == $szSortFieldId )
$myDBSortedFieldName = "ItemCount";
else
$myDBSortedFieldName = $szSortFieldId;
// ---
// Special handling for date fields
if ( $nConsFieldType == FILTER_TYPE_DATE )
{
// Helper variable for the select statement
$mySelectFieldName = $myDBGroupByFieldName . "Grouped";
$myDBQueryFieldName = "DATE( " . $myDBConsFieldName . ") AS " . $myDBGroupByFieldName ;
}
// Set Limit String
if ( $nRecordLimit > 0 )
$szLimitSql = " LIMIT " . $nRecordLimit;
else
$szLimitSql = "";
// Create SQL Where Clause!
if ( $this->_SQLwhereClause == "" )
{
$res = $this->CreateSQLWhereClause();
if ( $res != SUCCESS )
return $res;
}
// Create SQL String now!
$szSql = "SELECT " .
$myDBQueryFields .
"count(" . $myDBConsFieldName . ") as ItemCount " .
" FROM " . $this->_logStreamConfigObj->DBTableName .
$this->_SQLwhereClause .
" GROUP BY " . $myDBGroupByFieldName .
" ORDER BY " . $myDBSortedFieldName . " " . $szSortingOrder .
$szLimitSql ;
// Perform Database Query
$myquery = mysql_query($szSql, $this->_dbhandle);
if ( !$myquery )
return ERROR_DB_QUERYFAILED;
// Initialize Array variable
$aResult = array();
// read data records
while ($myRow = mysql_fetch_array($myquery, MYSQL_ASSOC))
{
// Create new row
$aNewRow = array();
foreach ( $myRow as $myFieldName => $myFieldValue )
{
if ( $myFieldName == $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId] )
$aNewRow[$szConsFieldId] = $myFieldValue;
else
$aNewRow[$myFieldName] = $myFieldValue;
}
// Add new row to result
$aResult[] = $aNewRow;
}
// return finished array
if ( count($aResult) > 0 )
return $aResult;
else
return ERROR_NOMORERECORDS;
}
/**
* Implementation of ConsolidateDataByField
*
* In the native MYSQL Logstream, the database will do most of the work
*
* @return integer Error stat
*/
public function ConsolidateDataByField($szConsFieldId, $nRecordLimit, $szSortFieldId, $nSortingOrder, $aIncludeCustomFields = null, $bIncludeLogStreamFields = false)
{
global $content, $dbmapping, $fields;
// Copy helper variables, this is just for better readability
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Check if fields are available
if ( !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId]) || !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szSortFieldId]) )
return ERROR_DB_DBFIELDNOTFOUND;
// --- Set Options
$nConsFieldType = $fields[$szConsFieldId]['FieldType'];
if ( $nSortingOrder == SORTING_ORDER_DESC )
$szSortingOrder = "DESC";
else
$szSortingOrder = "ASC";
// ---
// --- Set DB Field names
$myDBConsFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId];
$myDBGroupByFieldName = $myDBConsFieldName;
// Check which fields to include
if ( $aIncludeCustomFields != null )
{
$myDBQueryFields = "";
foreach ( $aIncludeCustomFields as $myFieldName )
{
if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName]) )
$myDBQueryFields .= $dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName] . ", ";
}
// Append Sortingfield
if ( !in_array($szConsFieldId, $aIncludeCustomFields) )
$myDBQueryFields .= $myDBConsFieldName . ", ";
}
else if ( $bIncludeLogStreamFields )
{
$myDBQueryFields = "";
foreach ( $this->_arrProperties as $myFieldName )
{
if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName]) )
$myDBQueryFields .= $dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName] . ", ";
}
}
else // Only Include ConsolidateField
$myDBQueryFields = $myDBConsFieldName . ", ";
if ( $szConsFieldId == $szSortFieldId )
$myDBSortedFieldName = "ItemCount";
else
$myDBSortedFieldName = $szSortFieldId;
// ---
// Special handling for date fields
if ( $nConsFieldType == FILTER_TYPE_DATE )
{
// Helper variable for the select statement
$mySelectFieldName = $myDBGroupByFieldName . "Grouped";
$myDBQueryFieldName = "DATE( " . $myDBConsFieldName . ") AS " . $myDBGroupByFieldName ;
}
// Set Limit String
if ( $nRecordLimit > 0 )
$szLimitSql = " LIMIT " . $nRecordLimit;
else
$szLimitSql = "";
// Create SQL Where Clause!
if ( $this->_SQLwhereClause == "" )
{
$res = $this->CreateSQLWhereClause();
if ( $res != SUCCESS )
return $res;
}
// Create SQL String now!
$szSql = "SELECT " .
$myDBQueryFields .
"count(" . $myDBConsFieldName . ") as ItemCount " .
" FROM " . $this->_logStreamConfigObj->DBTableName .
$this->_SQLwhereClause .
" GROUP BY " . $myDBGroupByFieldName .
" ORDER BY " . $myDBSortedFieldName . " " . $szSortingOrder .
$szLimitSql ;
// Perform Database Query
$myquery = mysql_query($szSql, $this->_dbhandle);
if ( !$myquery )
return ERROR_DB_QUERYFAILED;
// Initialize Array variable
$aResult = array();
// read data records
while ($myRow = mysql_fetch_array($myquery, MYSQL_ASSOC))
{
// Create new row
$aNewRow = array();
foreach ( $myRow as $myFieldName => $myFieldValue )
{
if ( $myFieldName == $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId] )
$aNewRow[$szConsFieldId] = $myFieldValue;
else
$aNewRow[$myFieldName] = $myFieldValue;
}
// Add new row to result
$aResult[] = $aNewRow;
}
// return finished array
if ( count($aResult) > 0 )
return $aResult;
else
return ERROR_NOMORERECORDS;
}
/**
* Implementation of GetCountSortedByField
*
* In the native MYSQL Logstream, the database will do most of the work
*
* @return integer Error stat
*/
public function GetCountSortedByField($szFieldId, $nFieldType, $nRecordLimit)
{
global $content, $dbmapping;
// Copy helper variables, this is just for better readability
$szTableType = $this->_logStreamConfigObj->DBTableType;
if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$szFieldId]) )
{
// Set DB Field name first!
$myDBFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szFieldId];
$myDBQueryFieldName = $myDBFieldName;
$mySelectFieldName = $myDBFieldName;
// Special handling for date fields
if ( $nFieldType == FILTER_TYPE_DATE )
{
// Helper variable for the select statement
$mySelectFieldName = $mySelectFieldName . "Grouped";
$myDBQueryFieldName = "DATE( " . $myDBFieldName . ") AS " . $mySelectFieldName ;
}
// Create SQL String now!
$szSql = "SELECT " .
$myDBQueryFieldName . ", " .
"count(" . $myDBFieldName . ") as TotalCount " .
" FROM " . $this->_logStreamConfigObj->DBTableName .
" GROUP BY " . $mySelectFieldName .
" ORDER BY TotalCount DESC" .
" LIMIT " . $nRecordLimit;
// Perform Database Query
$myquery = mysql_query($szSql, $this->_dbhandle);
if ( !$myquery )
return ERROR_DB_QUERYFAILED;
// Initialize Array variable
$aResult = array();
// read data records
while ($myRow = mysql_fetch_array($myquery, MYSQL_ASSOC))
$aResult[ $myRow[$mySelectFieldName] ] = $myRow['TotalCount'];
// return finished array
if ( count($aResult) > 0 )
return $aResult;
else
return ERROR_NOMORERECORDS;
}
else
{
// return error code, field mapping not found
return ERROR_DB_DBFIELDNOTFOUND;
}
}
/*
* ============= Beginn of private functions =============
*/
/*
* This function expects the filters to already being set earlier.
* Otherwise no usual WHERE Clause can be created!
*/
private function CreateSQLWhereClause()
{
if ( $this->_filters != null )
{
global $dbmapping;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Reset WhereClause
$this->_SQLwhereClause = "";
// Loop through all available properties
foreach( $this->_arrProperties as $propertyname )
{
// If the property exists in the filter array, we have something to filter for ^^!
if ( array_key_exists($propertyname, $this->_filters) )
{
// Process all filters
foreach( $this->_filters[$propertyname] as $myfilter )
{
// Only perform if database mapping is available for this filter!
if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$propertyname]) )
{
switch( $myfilter[FILTER_TYPE] )
{
case FILTER_TYPE_STRING:
// --- Either make a LIKE or a equal query!
if ( $myfilter[FILTER_MODE] & FILTER_MODE_SEARCHFULL )
{
// Set addnot to nothing
$addnod = "";
// --- Check if user wants to include or exclude!
if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
{
$szSearchBegin = " = '";
$szSearchEnd = "' ";
}
else
{
$szSearchBegin = " <> '";
$szSearchEnd = "' ";
}
// ---
}
else if ( $myfilter[FILTER_MODE] & FILTER_MODE_SEARCHREGEX )
{
// --- Check if user wants to include or exclude!
if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
$addnod = "";
else
$addnod = " NOT";
// ---
$szSearchBegin = " REGEXP '";
$szSearchEnd = "' ";
}
else
{
// --- Check if user wants to include or exclude!
if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
$addnod = "";
else
$addnod = " NOT";
// ---
$szSearchBegin = " LIKE '%";
$szSearchEnd = "%' ";
}
// ---
// --- If Syslog message, we have AND handling, otherwise OR!
if ( $propertyname == SYSLOG_MESSAGE )
$addor = " AND ";
else
{
// If we exclude filters, we need to combine with AND
if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
$addor = " OR ";
else
$addor = " AND ";
}
// ---
// Now Create LIKE Filters
if ( isset($tmpfilters[$propertyname]) )
$tmpfilters[$propertyname][FILTER_VALUE] .= $addor . $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . $addnod . $szSearchBegin . DB_RemoveBadChars($myfilter[FILTER_VALUE]) . $szSearchEnd;
else
{
$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_STRING;
$tmpfilters[$propertyname][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . $addnod . $szSearchBegin . DB_RemoveBadChars($myfilter[FILTER_VALUE]) . $szSearchEnd;
}
break;
case FILTER_TYPE_NUMBER:
// --- Check if user wants to include or exclude!
if ( $myfilter[FILTER_MODE] & FILTER_MODE_EXCLUDE )
{
// Add to filterset
$szArrayKey = $propertyname . "-NOT";
if ( isset($tmpfilters[$szArrayKey]) )
$tmpfilters[$szArrayKey][FILTER_VALUE] .= ", " . $myfilter[FILTER_VALUE];
else
{
$tmpfilters[$szArrayKey][FILTER_TYPE] = FILTER_TYPE_NUMBER;
$tmpfilters[$szArrayKey][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " NOT IN (" . DB_RemoveBadChars($myfilter[FILTER_VALUE]);
}
}
else
{
// Add to filterset
if ( isset($tmpfilters[$propertyname]) )
$tmpfilters[$propertyname][FILTER_VALUE] .= ", " . $myfilter[FILTER_VALUE];
else
{
$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_NUMBER;
$tmpfilters[$propertyname][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " IN (" . DB_RemoveBadChars($myfilter[FILTER_VALUE]);
}
}
// ---
break;
case FILTER_TYPE_DATE:
if ( isset($tmpfilters[$propertyname]) )
$tmpfilters[$propertyname][FILTER_VALUE] .= " AND ";
else
{
$tmpfilters[$propertyname][FILTER_VALUE] = "";
$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_DATE;
}
if ( $myfilter[FILTER_DATEMODE] == DATEMODE_LASTX )
{
// Get current timestamp
$nNowTimeStamp = time();
if ( $myfilter[FILTER_VALUE] == DATE_LASTX_HOUR )
$nNowTimeStamp -= 60 * 60; // One Hour!
else if ( $myfilter[FILTER_VALUE] == DATE_LASTX_12HOURS )
$nNowTimeStamp -= 60 * 60 * 12; // 12 Hours!
else if ( $myfilter[FILTER_VALUE] == DATE_LASTX_24HOURS )
$nNowTimeStamp -= 60 * 60 * 24; // 24 Hours!
else if ( $myfilter[FILTER_VALUE] == DATE_LASTX_7DAYS )
$nNowTimeStamp -= 60 * 60 * 24 * 7; // 7 days
else if ( $myfilter[FILTER_VALUE] == DATE_LASTX_31DAYS )
$nNowTimeStamp -= 60 * 60 * 24 * 31; // 31 days
else
{
// Set filter to unknown and Abort in this case!
$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_UNKNOWN;
break;
}
// Append filter
$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " > '" . date("Y-m-d H:i:s", $nNowTimeStamp) . "'";
}
else if ( $myfilter[FILTER_DATEMODE] == DATEMODE_RANGE_FROM )
{
// Obtain Event struct for the time!
$myeventtime = GetEventTime($myfilter[FILTER_VALUE]);
$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " > '" . date("Y-m-d H:i:s", $myeventtime[EVTIME_TIMESTAMP]) . "'";
}
else if ( $myfilter[FILTER_DATEMODE] == DATEMODE_RANGE_TO )
{
// Obtain Event struct for the time!
$myeventtime = GetEventTime($myfilter[FILTER_VALUE]);
$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " < '" . date("Y-m-d H:i:s", $myeventtime[EVTIME_TIMESTAMP]) . "'";
}
break;
default:
// Nothing to do!
break;
}
}
else
{
// Check how to treat not found db mappings / filters
if ( GetConfigSetting("TreatNotFoundFiltersAsTrue", 0, CFGLEVEL_USER) == 0 )
return ERROR_DB_DBFIELDNOTFOUND;
}
}
}
}
// Check and combine all filters now!
if ( isset($tmpfilters) )
{
// Append filters
foreach( $tmpfilters as $tmpfilter )
{
// Init WHERE or Append AND
if ( strlen($this->_SQLwhereClause) > 0 )
$this->_SQLwhereClause .= " AND ";
else
$this->_SQLwhereClause = " WHERE ";
switch( $tmpfilter[FILTER_TYPE] )
{
case FILTER_TYPE_STRING:
$this->_SQLwhereClause .= "( " . $tmpfilter[FILTER_VALUE] . ") ";
break;
case FILTER_TYPE_NUMBER:
$this->_SQLwhereClause .= $tmpfilter[FILTER_VALUE] . ") ";
break;
case FILTER_TYPE_DATE:
$this->_SQLwhereClause .= $tmpfilter[FILTER_VALUE];
break;
default:
// Should not happen, wrong filters!
// We add a dummy into the where clause, just as a place holder
$this->_SQLwhereClause .= " 1=1 ";
break;
}
}
}
//echo $this->_SQLwhereClause;
//$dbmapping[$szTableType][SYSLOG_UID]
}
else // No filters means nothing to do!
return SUCCESS;
}
/*
* Destroy the SQL QUery!
*/
private function DestroyMainSQLQuery()
{
// create query if necessary!
if ( $this->_myDBQuery != null )
{
// Free Query ressources
mysql_free_result ($this->_myDBQuery);
$this->_myDBQuery = null;
}
// return success state if reached this point!
return SUCCESS;
}
/*
* This helper function will read the next records into the buffer.
*/
private function ReadNextRecordsFromDB($uID)
{
global $querycount;
// Clear SQL Query first!
$this->DestroyMainSQLQuery();
// return error if there was one!
if ( ($res = $this->CreateMainSQLQuery($uID)) != SUCCESS )
return $res;
// Append LIMIT clause
// $szSql .= " LIMIT " . $this->_currentRecordStart . ", " . $this->_logStreamConfigObj->RecordsPerQuery;
// Copy rows into the buffer!
$iBegin = $this->_currentRecordNum;
while ($myRow = mysql_fetch_array($this->_myDBQuery, MYSQL_ASSOC))
{
// Check if result was successfull!
if ( $myRow === FALSE || !$myRow )
break;
// Keys will be converted into lowercase!
$this->bufferedRecords[$iBegin] = array_change_key_case($myRow, CASE_LOWER);
$iBegin++;
}
// --- Check if results were found
if ( $iBegin == $this->_currentRecordNum )
return ERROR_NOMORERECORDS;
// ---
// Free Query ressources
// mysql_free_result ($myquery);
// Only obtain count if enabled and not done before
if ( $this->_logStreamConfigObj->DBEnableRowCounting && $this->_totalRecordCount == -1 )
{
$this->_totalRecordCount = $this->GetRowCountFromTable();
if ( $this->_totalRecordCount <= 0 )
return ERROR_NOMORERECORDS;
}
// Increment for the Footer Stats
$querycount++;
// return success state if reached this point!
return SUCCESS;
}
/*
* Create the SQL QUery!
*/
private function CreateMainSQLQuery($uID)
{
global $querycount;
// Get SQL Statement
$szSql = $this->CreateSQLStatement($uID);
// --- Append LIMIT
$szSql .= " LIMIT " . $this->_logStreamConfigObj->RecordsPerQuery;
// ---
// Perform Database Query
$this->_myDBQuery = mysql_query($szSql, $this->_dbhandle);
if ( !$this->_myDBQuery )
{
$this->PrintDebugError("Invalid SQL: ".$szSql);
return ERROR_DB_QUERYFAILED;
}
else
{
// Skip one entry in this case
if ( $this->_currentRecordStart > 0 )
{
// Throw away
$myRow = mysql_fetch_array($this->_myDBQuery, MYSQL_ASSOC);
}
}
// Increment for the Footer Stats
$querycount++;
// Output Debug Informations
OutputDebugMessage("LogStreamDB|CreateMainSQLQuery: Created SQL Query:<br>" . $szSql, DEBUG_DEBUG);
// return success state if reached this point!
return SUCCESS;
}
/*
* Creates the SQL Statement we are going to use!
*/
private function CreateSQLStatement($uID, $includeFields = true)
{
global $dbmapping;
// Copy helper variables, this is just for better readability
$szTableType = $this->_logStreamConfigObj->DBTableType;
$szSortColumn = $this->_logStreamConfigObj->SortColumn;
// Create Basic SQL String
if ( $this->_logStreamConfigObj->DBEnableRowCounting ) // with SQL_CALC_FOUND_ROWS
$sqlString = "SELECT SQL_CALC_FOUND_ROWS " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID];
else // without row calc
$sqlString = "SELECT " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID];
// Append fields if needed
if ( $includeFields && $this->_arrProperties != null )
{
// Loop through all requested fields
foreach ( $this->_arrProperties as $myproperty )
{
// SYSLOG_UID already added!
if ( $myproperty != SYSLOG_UID && isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) )
{
// Append field!
$sqlString .= ", " . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty];
}
}
}
// Append FROM 'table'!
$sqlString .= " FROM " . $this->_logStreamConfigObj->DBTableName;
// Append precreated where clause
$sqlString .= $this->_SQLwhereClause;
// Append UID QUERY!
if ( $uID != -1 )
{
if ( $this->_readDirection == EnumReadDirection::Forward )
$myOperator = ">=";
else
$myOperator = "<=";
if ( strlen($this->_SQLwhereClause) > 0 )
$sqlString .= " AND " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " $myOperator $uID";
else
$sqlString .= " WHERE " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " $myOperator $uID";
}
// Append ORDER clause
if ( $this->_readDirection == EnumReadDirection::Forward )
$sqlString .= " ORDER BY " . $dbmapping[$szTableType]['DBMAPPINGS'][$szSortColumn];
else if ( $this->_readDirection == EnumReadDirection::Backward )
$sqlString .= " ORDER BY " . $dbmapping[$szTableType]['DBMAPPINGS'][$szSortColumn] . " DESC";
// return SQL result string:
return $sqlString;
}
/*
* Reset record buffer in this function!
*/
private function ResetBufferedRecords()
{
if ( isset($this->bufferedRecords) )
{
// Loop through all subrecords first!
foreach ($this->bufferedRecords as $mykey => $myrecord)
unset( $this->bufferedRecords[$mykey] );
// Set buffered records to NULL!
$this->bufferedRecords = null;
}
}
/*
* Helper function to display SQL Errors for now!
*/
private function PrintDebugError($szErrorMsg)
{
$errdesc = mysql_error();
$errno = mysql_errno();
$errormsg="$szErrorMsg <br>";
$errormsg.="Detail error: $errdesc <br>";
$errormsg.="Error Code: $errno <br>";
//Output!
OutputDebugMessage("LogStreamDB|PrintDebugError: $errormsg", DEBUG_ERROR);
}
/*
* Returns the number of possible records by using a query
*/
private function GetRowCountByString($szQuery)
{
if ($myQuery = mysql_query($szQuery))
{
$num_rows = mysql_num_rows($myQuery);
mysql_free_result ($myQuery);
}
return $num_rows;
}
/*
* Returns the number of possible records by using an existing queryid
*/
private function GetRowCountByQueryID($myQuery)
{
$num_rows = mysql_num_rows($myQuery);
return $num_rows;
}
/*
* Returns the number of possible records by using a select count statement!
*/
private function GetRowCountFromTable()
{
if ( $myquery = mysql_query("Select FOUND_ROWS();", $this->_dbhandle) )
{
// Get first and only row!
$myRow = mysql_fetch_array($myquery);
// copy row count
$numRows = $myRow[0];
}
else
$numRows = -1;
// return result!
return $numRows;
/* OLD slow code!
global $dbmapping,$querycount;
$szTableType = $this->_logStreamConfigObj->DBTableType;
// Create Statement and perform query!
$szSql = "SELECT count(" . $dbmapping[$szTableType][SYSLOG_UID] . ") FROM " . $this->_logStreamConfigObj->DBTableName . $this->_SQLwhereClause;
if ($myQuery = mysql_query($szSql, $this->_dbhandle))
{
// obtain first and only row
$myRow = mysql_fetch_row($myQuery);
$numRows = $myRow[0];
// Increment for the Footer Stats
$querycount++;
// Free query now
mysql_free_result ($myQuery);
}
else
$numRows = -1;
*/
}
}
?>