1<?php
2/*
3	*********************************************************************
4	* LogAnalyzer - http://loganalyzer.adiscon.com
5	* -----------------------------------------------------------------	*
6	* Some constants													*
7	*																	*
8	* LogStreamDB provides access to the data in database. In the most
9	* cases this will be plain text files. If we need access to e.g.
10	* zipped files, this will be handled by a separate driver.
11	*
12	* \version 2.0.0 Init Version
13	*																	*
14	* All directives are explained within this file						*
15	*
16	* Copyright (C) 2008-2010 Adiscon GmbH.
17	*
18	* This file is part of LogAnalyzer.
19	*
20	* LogAnalyzer is free software: you can redistribute it and/or modify
21	* it under the terms of the GNU General Public License as published by
22	* the Free Software Foundation, either version 3 of the License, or
23	* (at your option) any later version.
24	*
25	* LogAnalyzer is distributed in the hope that it will be useful,
26	* but WITHOUT ANY WARRANTY; without even the implied warranty of
27	* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
28	* GNU General Public License for more details.
29	*
30	* You should have received a copy of the GNU General Public License
31	* along with LogAnalyzer. If not, see <http://www.gnu.org/licenses/>.
32	*
33	* A copy of the GPL can be found in the file "COPYING" in this
34	* distribution.
35	*********************************************************************
36*/
37
38// --- Avoid directly accessing this file!
39if ( !defined('IN_PHPLOGCON') )
40{
41	die('Hacking attempt');
42	exit;
43}
44// ---
45
46// --- Required Includes!
47require_once($gl_root_path . 'include/constants_errors.php');
48// ---
49
50class LogStreamDB extends LogStream {
51	private $_dbhandle = null;
52
53	// Helper to store the database records
54	private $bufferedRecords = null;
55	private $_currentRecordStart = 0;
56	private $_currentRecordNum = 0;
57	private $_totalRecordCount = -1;
58	private $_previousPageUID = -1;
59	private $_lastPageUID = -1;
60	private $_firstPageUID = -1;
61	private $_currentPageNumber = -1;
62
63	private $_SQLwhereClause = "";
64	private $_myDBQuery = null;
65
66	// Constructor
67	public function __construct ($streamConfigObj) {
68		$this->_logStreamConfigObj = $streamConfigObj;
69
70		if ( $this->_logStreamConfigObj->DBType == DB_MYSQL )
71		{
72			// Probe if a function exists!
73			if ( !function_exists("mysqli_connect") )
74				DieWithFriendlyErrorMsg("Error, MYSQL Extensions are not enabled! Function 'mysqli_connect' does not exist.");
75		}
76	}
77	public function LogStreamDB($streamConfigObj) {
78		self::__construct($streamConfigObj);
79	}
80
81	/**
82	* Open and verifies the database conncetion
83	*
84	* @param arrProperties array in: Properties wish list.
85	* @return integer Error stat
86	*/
87	public function Open($arrProperties)
88	{
89		global $dbmapping;
90
91		// Initialise Basic stuff within the Classs
92		$this->RunBasicInits();
93
94		// Verify database connection (This also opens the database!)
95		$res = $this->Verify();
96		if ( $res != SUCCESS )
97			return $res;
98
99		// Copy the Property Array
100		$this->_arrProperties = $arrProperties;
101
102		// Check if DB Mapping exists
103		if ( !isset($dbmapping[ $this->_logStreamConfigObj->DBTableType ]) )
104			return ERROR_DB_INVALIDDBMAPPING;
105
106		// Create SQL Where Clause first!
107		$res = $this->CreateSQLWhereClause();
108		if ( $res != SUCCESS )
109			return $res;
110
111		// Success, this means we init the Pagenumber to ONE!
112		// $this->_currentPageNumber = 1;
113
114		// reached this point means success!
115		return SUCCESS;
116	}
117
118	/*
119	*	Helper function to clear the current querystring!
120	*/
121	public function ResetFilters()
122	{
123		// Clear _SQLwhereClause variable!
124		$this->_SQLwhereClause = "";
125	}
126
127	/**
128	* Close the database connection.
129	*
130	* @return integer Error state
131	*/
132	public function Close()
133	{
134		if ($this->_dbhandle)
135			mysqli_close($this->_dbhandle);
136		$this->_dbhandle = null;
137		return SUCCESS;
138	}
139
140	/**
141	* Verify if the database connection exists!
142	*
143	* @return integer Error state
144	*/
145	public function Verify() {
146		// Try to connect to the database
147		if ( $this->_dbhandle == null )
148		{
149			// Forces to open a new link in all cases!
150			$this->_dbhandle = @mysqli_connect($this->_logStreamConfigObj->DBServer,$this->_logStreamConfigObj->DBUser,$this->_logStreamConfigObj->DBPassword);
151			if (!$this->_dbhandle)
152			{
153				if ( isset($php_errormsg) )
154				{
155					global $extraErrorDescription;
156					$extraErrorDescription = $php_errormsg;
157				}
158
159				// Return error code
160				return ERROR_DB_CONNECTFAILED;
161			}
162		}
163
164		// Select the database now!
165		$bRet = @mysqli_select_db($this->_dbhandle, $this->_logStreamConfigObj->DBName);
166		if(!$bRet)
167		{
168			if ( isset($php_errormsg) )
169			{
170				global $extraErrorDescription;
171				$extraErrorDescription = $php_errormsg;
172			}
173
174			// Return error code
175			return ERROR_DB_CANNOTSELECTDB;
176		}
177
178		// Check if the table exists!
179		$numTables = @mysqli_num_rows( mysqli_query($this->_dbhandle, "SHOW TABLES LIKE '%" . $this->_logStreamConfigObj->DBTableName . "%'"));
180		if( $numTables <= 0 )
181			return ERROR_DB_TABLENOTFOUND;
182
183		// reached this point means success ;)!
184		return SUCCESS;
185	}
186
187
188	/*
189	*	Implementation of VerifyFields: Checks if fields exist in table
190	*/
191	public function VerifyFields( $arrProperitesIn )
192	{
193		global $dbmapping, $fields;
194
195		// Get List of Indexes as Array
196		$arrFieldKeys = $this->GetFieldsAsArray();
197		$szTableType = $this->_logStreamConfigObj->DBTableType;
198
199		// Loop through all fields to see which one is missing!
200		foreach ( $arrProperitesIn as $myproperty )
201		{
202//			echo $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "<br>";
203			if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) && in_array($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], $arrFieldKeys) )
204			{
205				OutputDebugMessage("LogStreamDB|VerifyFields: Found Field for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "'", DEBUG_ULTRADEBUG);
206				continue;
207			}
208			else
209			{
210				// Index is missing for this field!
211				OutputDebugMessage("LogStreamDB|VerifyFields: Missing Field for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "'", DEBUG_WARN);
212				return ERROR_DB_DBFIELDNOTFOUND;
213			}
214		}
215
216		// Successfull
217		return SUCCESS;
218	}
219
220
221	/*
222	*	Implementation of VerifyIndexes: Checks if indexes exist for desired fields
223	*/
224	public function VerifyIndexes( $arrProperitesIn )
225	{
226		global $dbmapping, $fields;
227
228		// Get List of Indexes as Array
229		$arrIndexKeys = $this->GetIndexesAsArray();
230		$szTableType = $this->_logStreamConfigObj->DBTableType;
231
232		// Loop through all fields to see which one is missing!
233		foreach ( $arrProperitesIn as $myproperty )
234		{
235//			echo $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "<br>";
236			if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) && in_array($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], $arrIndexKeys) )
237			{
238				OutputDebugMessage("LogStreamDB|VerifyIndexes: Found INDEX for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "'", DEBUG_ULTRADEBUG);
239				continue;
240			}
241			else
242			{
243				// Index is missing for this field!
244				OutputDebugMessage("LogStreamDB|VerifyIndexes: Missing INDEX for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "'", DEBUG_WARN);
245				return ERROR_DB_INDEXESMISSING;
246			}
247		}
248
249		// Successfull
250		return SUCCESS;
251	}
252
253
254	/*
255	*	Implementation of VerifyChecksumTrigger: Checks if checksum trigger exists
256	*/
257	public function VerifyChecksumTrigger( $myTriggerProperty )
258	{
259		global $dbmapping, $fields;
260
261		// Get List of Triggers as Array
262		$arrIndexTriggers = $this->GetTriggersAsArray();
263
264		$szTableType = $this->_logStreamConfigObj->DBTableType;
265		$szDBName = $this->_logStreamConfigObj->DBName;
266		$szTableName = $this->_logStreamConfigObj->DBTableName;
267		$szDBTriggerField = $dbmapping[$szTableType]['DBMAPPINGS'][$myTriggerProperty];
268
269		// Create Triggername | lowercase!
270		$szTriggerName = strtolower( $szDBName . "_" . $szTableName . "_" . $szDBTriggerField );
271
272		// Try to find logstream trigger
273		if ( count($arrIndexTriggers) > 0 )
274		{
275			if ( in_array($szTriggerName, $arrIndexTriggers) )
276				return SUCCESS;
277			else
278			{
279				// Index is missing for this field!
280				OutputDebugMessage("LogStreamDB|VerifyChecksumTrigger: Missing TRIGGER '" . $szTriggerName . "' for Table '" . $szTableName . "'", DEBUG_WARN);
281				return ERROR_DB_TRIGGERMISSING;
282			}
283		}
284		else
285		{
286			// Index is missing for this field!
287			OutputDebugMessage("LogStreamDB|VerifyChecksumTrigger: No TRIGGERS found in your database", DEBUG_WARN);
288			return ERROR_DB_TRIGGERMISSING;
289		}
290	}
291
292
293	/*
294	*	Implementation of CreateMissingIndexes: Checks if indexes exist for desired fields
295	*/
296	public function CreateMissingIndexes( $arrProperitesIn )
297	{
298		global $dbmapping, $fields, $querycount;
299
300		// Get List of Indexes as Array
301		$arrIndexKeys = $this->GetIndexesAsArray();
302		$szTableType = $this->_logStreamConfigObj->DBTableType;
303
304		// Loop through all fields to see which one is missing!
305		foreach ( $arrProperitesIn as $myproperty )
306		{
307			if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) && in_array($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], $arrIndexKeys) )
308				continue;
309			else
310			{
311				// Update Table schema now!
312				$szSql = "ALTER TABLE " . $this->_logStreamConfigObj->DBTableName . " ADD INDEX ( " . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . " )";
313
314				// Index is missing for this field!
315				OutputDebugMessage("LogStreamDB|CreateMissingIndexes: Createing missing INDEX for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "' - " . $szSql, DEBUG_INFO);
316
317				// Add missing INDEX now!
318				$myQuery = mysqli_query($this->_dbhandle, $szSql);
319				if (!$myQuery)
320				{
321					// Return failure!
322					$this->PrintDebugError("Dynamically Adding INDEX for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "' failed with Statement: '" . $szSql . "'");
323					return ERROR_DB_INDEXFAILED;
324				}
325			}
326		}
327
328		// Successfull
329		return SUCCESS;
330	}
331
332
333	/*
334	*	Implementation of CreateMissingFields: Checks if indexes exist for desired fields
335	*/
336	public function CreateMissingFields( $arrProperitesIn )
337	{
338		global $dbmapping, $fields, $querycount;
339
340		// Get List of Indexes as Array
341		$arrFieldKeys = $this->GetFieldsAsArray();
342		$szTableType = $this->_logStreamConfigObj->DBTableType;
343
344		// Loop through all fields to see which one is missing!
345		foreach ( $arrProperitesIn as $myproperty )
346		{
347			if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) && in_array($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], $arrFieldKeys) )
348				continue;
349			else
350			{
351				if ( $this->HandleMissingField( $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], $arrProperitesIn ) == SUCCESS )
352				{
353					// Index is missing for this field!
354					OutputDebugMessage("LogStreamDB|CreateMissingFields: Createing missing FIELD for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty], DEBUG_INFO);
355				}
356				else
357				{
358					// Return failure!
359					$this->PrintDebugError("Dynamically Adding FIELD for '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "' failed!");
360					return ERROR_DB_ADDDBFIELDFAILED;
361				}
362			}
363		}
364
365		// Successfull
366		return SUCCESS;
367	}
368
369
370	/*
371	*	Implementation of GetCreateMissingTriggerSQL: Creates SQL needed to create a TRIGGER
372	*/
373	public function GetCreateMissingTriggerSQL( $myDBTriggerField, $myDBTriggerCheckSumField )
374	{
375		global $dbmapping, $fields, $querycount;
376
377		// Get List of Triggers as Array
378		$szDBName = $this->_logStreamConfigObj->DBName;
379		$szTableName = $this->_logStreamConfigObj->DBTableName;
380
381		// Create Triggername
382		$szTriggerName = $szDBName . "_" . $szTableName . "_" . $myDBTriggerField;
383
384		// Create TRIGGER SQL!
385		$szSql =	"CREATE TRIGGER " . $szTriggerName . " BEFORE INSERT ON `" . $szTableName . "`
386					 FOR EACH ROW
387					 BEGIN
388					 SET NEW." . $myDBTriggerCheckSumField . " = crc32(NEW." . $myDBTriggerField . ");
389					 END
390					;";
391
392		return $szSql;
393	}
394
395
396	/*
397	*	Implementation of CreateMissingTrigger: Creates missing triggers !
398	*/
399	public function CreateMissingTrigger( $myTriggerProperty, $myCheckSumProperty )
400	{
401		global $dbmapping, $fields, $querycount;
402
403		// Get List of Triggers as Array
404		$szTableName = $this->_logStreamConfigObj->DBTableName;
405		$szTableType = $this->_logStreamConfigObj->DBTableType;
406		$szDBTriggerField = $dbmapping[$szTableType]['DBMAPPINGS'][$myTriggerProperty];
407		$szDBTriggerCheckSumField = $dbmapping[$szTableType]['DBMAPPINGS'][$myCheckSumProperty];
408
409		// Get SQL Code to create the trigger!
410		$szSql = $this->GetCreateMissingTriggerSQL( $szDBTriggerField, $szDBTriggerCheckSumField );
411
412		// Index is missing for this field!
413		OutputDebugMessage("LogStreamDB|CreateMissingTrigger: Creating missing TRIGGER for '" . $szTableName . "' - $szDBTriggerCheckSumField = crc32(NEW.$szDBTriggerField)" . $szSql, DEBUG_INFO);
414
415		// Add missing INDEX now!
416		$myQuery = mysqli_query($this->_dbhandle, $szSql);
417		if (!$myQuery)
418		{
419			// Return failure!
420			$this->PrintDebugError("Dynamically Adding TRIGGER for '" . $szTableName . "' failed!<br/><br/>If you want to manually add the TRIGGER, use the following SQL Command:<br/> " . str_replace("\n", "<br/>", $szSql) . "<br/>");
421
422			return ERROR_DB_TRIGGERFAILED;
423		}
424
425		// Successfull
426		return SUCCESS;
427	}
428
429
430	/*
431	*	Implementation of ChangeChecksumFieldUnsigned: Changes the Checkusm field to unsigned!
432	*/
433	public function ChangeChecksumFieldUnsigned()
434	{
435		global $dbmapping, $fields, $querycount;
436
437		// Get variables
438		$szTableType = $this->_logStreamConfigObj->DBTableType;
439
440		// Change Checksumfield to use UNSIGNED!
441		$szUpdateSql = "ALTER TABLE `" . $this->_logStreamConfigObj->DBTableName . "` CHANGE `" .
442						$dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . "` `" .
443						$dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . "` INT(11) UNSIGNED NOT NULL DEFAULT '0'";
444
445		// Update Table schema now!
446		$myQuery = mysqli_query($this->_dbhandle, $szUpdateSql);
447		if (!$myQuery)
448		{
449			// Return failure!
450			$this->PrintDebugError("ER_BAD_FIELD_ERROR - Failed to Change field '" . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . "' from signed to unsigned with sql statement: '" . $szUpdateSql . "'");
451			return ERROR_DB_CHECKSUMCHANGEFAILED;
452		}
453
454		// return results
455		return SUCCESS;
456	}
457
458
459	/*
460	*	Implementation of VerifyChecksumField: Verifies if the checkusm field is signed or unsigned!
461	*/
462	public function VerifyChecksumField()
463	{
464		global $dbmapping, $fields, $querycount;
465
466		// Get variables
467		$szTableType = $this->_logStreamConfigObj->DBTableType;
468
469		// Create SQL and Get INDEXES for table!
470		$szSql = "SHOW COLUMNS FROM `" . $this->_logStreamConfigObj->DBTableName . "` WHERE Field = '" . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . "'";
471		$myQuery = mysqli_query($this->_dbhandle, $szSql);
472		if ($myQuery)
473		{
474			// Get result!
475			$myRow = mysqli_fetch_array($myQuery,  MYSQLI_ASSOC);
476			if (strpos( strtolower($myRow['Type']), "unsigned") === false )
477			{
478				// return error code!
479				return ERROR_DB_CHECKSUMERROR;
480			}
481
482			// Free query now
483			mysqli_free_result ($myQuery);
484
485			// Increment for the Footer Stats
486			$querycount++;
487		}
488
489		// return results
490		return SUCCESS;
491	}
492
493
494	/**
495	* Read the data from a specific uID which means in this
496	* case beginning with from the Database ID
497	*
498	* @param uID integer in/out: unique id of the data row
499	* @param arrProperitesOut array out: array filled with properties
500	* @return integer Error state
501	* @see ReadNext()
502	*/
503	public function Read($uID, &$arrProperitesOut)
504	{
505		// Seek the first uID!
506		if ( $this->Sseek($uID, EnumSeek::UID, 0) == SUCCESS)
507		{
508			// Read the next record!
509			$ret = $this->ReadNext($uID, $arrProperitesOut);
510		}
511		else
512			$ret = ERROR_NOMORERECORDS;
513
514		// return result!
515		return $ret;
516	}
517
518	/**
519	* Read the next line from the file depending on the current
520	* read direction.
521	*
522	* Hint: If the current stream becomes unavailable an error
523	* stated is retuned. A typical case is if a log rotation
524	* changed the original data source.
525	*
526	* @param uID integer out: uID is the offset of data row
527	* @param arrProperitesOut array out: properties
528	* @return integer Error state
529	* @see ReadNext
530	*/
531	public function ReadNext(&$uID, &$arrProperitesOut, $bParseMessage = true)
532	{
533		// Helpers needed for DB Mapping
534		global $content, $gl_starttime;
535		global $dbmapping, $fields;
536		$szTableType = $this->_logStreamConfigObj->DBTableType;
537
538		// define $ret
539		$ret = SUCCESS;
540
541		do
542		{
543			// No buffer? then read from DB!
544			if ( $this->bufferedRecords == null )
545				$ret = $this->ReadNextRecordsFromDB($uID);
546			else
547			{
548				if ( !isset($this->bufferedRecords[$this->_currentRecordNum] ) )
549				{
550					// We need to load new records, so clear the old ones first!
551					$this->ResetBufferedRecords();
552
553					// Set new Record start, will be used in the SQL Statement!
554					$this->_currentRecordStart = $this->_currentRecordNum; // + 1;
555
556					// Now read new ones
557					$ret = $this->ReadNextRecordsFromDB($uID);
558
559					// Check if we found more records
560					if ( !isset($this->bufferedRecords[$this->_currentRecordNum] ) )
561						$ret = ERROR_NOMORERECORDS;
562				}
563			}
564
565			if ( $ret == SUCCESS && $this->_arrProperties != null )
566			{
567				// Init and set variables
568				foreach ( $this->_arrProperties as $property )
569				{
570					// Check if mapping exists
571					if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$property]) )
572					{
573						// Copy property if available!
574						$dbfieldname = $dbmapping[$szTableType]['DBMAPPINGS'][$property];
575						if ( isset($this->bufferedRecords[$this->_currentRecordNum][$dbfieldname]) )
576						{
577							if ( isset($fields[$property]['FieldType']) && $fields[$property]['FieldType'] == FILTER_TYPE_DATE ) // Handle as date!
578								$arrProperitesOut[$property] = GetEventTime( $this->bufferedRecords[$this->_currentRecordNum][$dbfieldname] );
579							else
580								$arrProperitesOut[$property] = $this->bufferedRecords[$this->_currentRecordNum][$dbfieldname];
581						}
582						else
583							$arrProperitesOut[$property] = '';
584					}
585					else
586						$arrProperitesOut[$property] = '';
587				}
588
589				// Run optional Message Parsers now
590				if ( isset($arrProperitesOut[SYSLOG_MESSAGE]) )
591				{
592					$retParser = $this->_logStreamConfigObj->ProcessMsgParsers($arrProperitesOut[SYSLOG_MESSAGE], $arrProperitesOut);
593
594					// Check if we have to skip the message!
595					if ( $retParser == ERROR_MSG_SKIPMESSAGE )
596						$ret = $retParser;
597				}
598
599				// Set uID to the PropertiesOut! //DEBUG -> $this->_currentRecordNum;
600				$uID = $arrProperitesOut[SYSLOG_UID] = $this->bufferedRecords[$this->_currentRecordNum][$dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID]];
601
602				// Increment $_currentRecordNum
603				$this->_currentRecordNum++;
604			}
605
606			// Check how long we are running. If only two seconds of execution time are left, we abort further reading!
607			$scriptruntime = intval(microtime_float() - $gl_starttime);
608			if ( $content['MaxExecutionTime'] > 0 && $scriptruntime > ($content['MaxExecutionTime']-2) )
609			{
610				// This may display a warning message, so the user knows we stopped reading records because of the script timeout.
611				$content['logstream_warning'] = "false";
612				$content['logstream_warning_details'] = $content['LN_WARNING_LOGSTREAMDISK_TIMEOUT'];
613				$content['logstream_warning_code'] = ERROR_FILE_NOMORETIME;
614
615				// Return error code
616				return ERROR_FILE_NOMORETIME;
617			}
618
619		// This additional filter check will take care on dynamic fields from the message parser!
620		} while ( $this->ApplyFilters($ret, $arrProperitesOut) != SUCCESS && $ret == SUCCESS );
621
622		// reached here means return result!
623		return $ret;
624	}
625
626	/**
627	* Implementation of Seek
628	*/
629	public function Sseek(&$uID, $mode, $numrecs)
630	{
631		// predefine return value
632		$ret = SUCCESS;
633
634		switch ($mode)
635		{
636			case EnumSeek::UID:
637//				if ( $uID == UID_UNKNOWN ) // set uID to first ID!
638				{
639					// No buffer? then read from DB!
640					if ( $this->bufferedRecords == null )
641						$ret = $this->ReadNextRecordsFromDB($uID);
642
643					if ( $ret == SUCCESS )
644					{
645						$this->_currentRecordNum = 0;
646						$uID = $this->bufferedRecords[ $this->_currentRecordNum ];
647					}
648				}
649				break;
650		}
651
652		// Return result!
653		return $ret;
654	}
655
656	/**
657	* GetMessageCount will return the count of Message.
658	* If this count is not available, the function will
659	* return the default -1
660	*/
661	public function GetMessageCount()
662	{
663		return $this->_totalRecordCount;
664	}
665
666	/**
667	* This function returns the first UID for previous PAGE, if availbale!
668	* Otherwise will return -1!
669	*/
670	public function GetPreviousPageUID()
671	{
672		return $this->_previousPageUID;
673	}
674
675	/**
676	* This function returns the FIRST UID for the FIRST PAGE!
677	* Will be done by a seperated SQL Statement.
678	*/
679	public function GetFirstPageUID()
680	{
681		global $querycount, $dbmapping;
682		$szTableType = $this->_logStreamConfigObj->DBTableType;
683
684		// Only perform query if row counting is enabled!
685		if ( strlen($this->_SQLwhereClause) > 0 && !$this->_logStreamConfigObj->DBEnableRowCounting )
686			return $this->_firstPageUID;
687
688		$szSql = "SELECT MAX(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") FROM `" .  $this->_logStreamConfigObj->DBTableName . "` " . $this->_SQLwhereClause;
689		$myQuery = mysqli_query($this->_dbhandle, $szSql);
690		if ($myQuery)
691		{
692			// obtain first and only row
693			$myRow = mysqli_fetch_row($myQuery);
694			$this->_firstPageUID = $myRow[0];
695
696			// Free query now
697			mysqli_free_result ($myQuery);
698
699			// Increment for the Footer Stats
700			$querycount++;
701		}
702
703		// Return result!
704		return $this->_firstPageUID;
705	}
706
707	/**
708	* This function returns the first UID for the last PAGE!
709	* Will be done by a seperated SQL Statement.
710	*/
711	public function GetLastPageUID()
712	{
713		global $querycount, $dbmapping;
714		$szTableType = $this->_logStreamConfigObj->DBTableType;
715
716		// Only perform query if row counting is enabled!
717		if ( strlen($this->_SQLwhereClause) > 0 && !$this->_logStreamConfigObj->DBEnableRowCounting )
718			return $this->_lastPageUID;
719
720		$szSql = "SELECT MIN(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") FROM `" .  $this->_logStreamConfigObj->DBTableName . "` " . $this->_SQLwhereClause;
721		$myQuery = mysqli_query($this->_dbhandle, $szSql);
722		if ($myQuery)
723		{
724			// obtain first and only row
725			$myRow = mysqli_fetch_row($myQuery);
726			$this->_lastPageUID = $myRow[0];
727
728			// Free query now
729			mysqli_free_result ($myQuery);
730
731			// Increment for the Footer Stats
732			$querycount++;
733		}
734
735		// Return result!
736		return $this->_lastPageUID;
737	}
738
739	/**
740	* This function returns the current Page number, if availbale!
741	* Otherwise will return 0! We also assume that this function is
742	* only called once DB is open!
743	*/
744	public function GetCurrentPageNumber()
745	{
746		return $this->_currentPageNumber;
747	}
748
749	/*
750	* Implementation of IsPropertySortable
751	*
752	* For now, sorting is only possible for the UID Property!
753	*/
754	public function IsPropertySortable($myProperty)
755	{
756		global $fields;
757
758		// TODO: HARDCODED | FOR NOW only FALSE!
759		return false;
760
761		if ( isset($fields[$myProperty]) && $myProperty == SYSLOG_UID )
762			return true;
763		else
764			return false;
765	}
766
767	/**
768	* Implementation of GetLogStreamStats
769	*
770	* Returns an Array og logstream statsdata
771	*	Count of Data Items
772	*	Total Filesize
773	*/
774	public function GetLogStreamStats()
775	{
776		global $querycount, $dbmapping;
777		$szTableType = $this->_logStreamConfigObj->DBTableType;
778
779		// Perform if Connection is true!
780		if ( $this->_dbhandle != null )
781		{
782			// Obtain Stats data for this table!
783			$szSql = "SHOW TABLE STATUS FROM `" .  $this->_logStreamConfigObj->DBName . "`";
784			$myQuery = mysqli_query($this->_dbhandle, $szSql);
785			if ($myQuery)
786			{
787				// Loop through results
788				while ($myRow = mysqli_fetch_array($myQuery,  MYSQLI_ASSOC))
789				{
790					// Set tablename!
791					$tableName = $myRow['Name'];
792					$myStats = null;
793					$myStats[]			= array( 'StatsDisplayName' => 'Table name', 'StatsValue' => $tableName );
794
795					// copy usefull statsdata
796					if ( isset($myRow['Engine']) )
797						$myStats[]		= array( 'StatsDisplayName' => 'Table engine', 'StatsValue' => $myRow['Engine'] );
798					if ( isset($myRow['Rows']) )
799						$myStats[]		= array( 'StatsDisplayName' => 'Rowcount', 'StatsValue' => $myRow['Rows'] );
800
801					if ( isset($myRow['Data_length']) )
802						$myStats[]		= array( 'StatsDisplayName' => 'Table filesize (bytes)', 'StatsValue' => $myRow['Data_length'] );
803					if ( isset($myRow['Collation']) )
804						$myStats[]		= array( 'StatsDisplayName' => 'Collation', 'StatsValue' => $myRow['Collation'] );
805					if ( isset($myRow['Comment']) )
806						$myStats[]		= array( 'StatsDisplayName' => 'Comment', 'StatsValue' => $myRow['Comment'] );
807
808					$stats[]['STATSDATA'] = $myStats;
809				}
810
811				// Free query now
812				mysqli_free_result ($myQuery);
813
814				// Increment for the Footer Stats
815				$querycount++;
816			}
817
818			// return results!
819			return $stats;
820		}
821		else
822			return null;
823	}
824
825	/**
826	* Implementation of GetLogStreamTotalRowCount
827	*
828	* Returns the total amount of rows in the main datatable
829	*/
830	public function GetLogStreamTotalRowCount()
831	{
832		global $querycount, $dbmapping;
833		$szTableType = $this->_logStreamConfigObj->DBTableType;
834
835		// Set default rowcount
836		$rowcount = null;
837
838		// Perform if Connection is true!
839		if ( $this->_dbhandle != null )
840		{
841			// SHOW TABLE STATUS FROM
842			$szSql = "SELECT count(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . ") as Counter FROM `" .  $this->_logStreamConfigObj->DBTableName . "`";
843			$myQuery = mysqli_query($this->_dbhandle, $szSql);
844			if ($myQuery)
845			{
846				// Obtain RowCount!
847				$myRow		= mysqli_fetch_row($myQuery);
848				$rowcount = $myRow[0];
849
850				// Free query now
851				mysqli_free_result ($myQuery);
852
853				// Increment for the Footer Stats
854				$querycount++;
855			}
856		}
857
858		//return result
859		return $rowcount;
860	}
861
862	/**
863	* Implementation of the CleanupLogdataByDate function! Returns affected rows!
864	*/
865	public function CleanupLogdataByDate( $nDateTimeStamp )
866	{
867		global $querycount, $dbmapping;
868		$szTableType = $this->_logStreamConfigObj->DBTableType;
869
870		// Set default rowcount
871		$rowcount = null;
872
873		// Perform if Connection is true!
874		if ( $this->_dbhandle != null )
875		{
876			// --- Init Filters if necessary!
877			if ( $this->_filters == null )
878				$this->SetFilter( "" ); // This will init filters!
879
880			// Create SQL Where Clause!
881			$this->CreateSQLWhereClause();
882			// ---
883
884			// --- Add default WHERE clause
885			if ( strlen($this->_SQLwhereClause) > 0 )
886				$szWhere = $this->_SQLwhereClause;
887			else
888				$szWhere = "";
889
890			// Add Datefilter if necessary!
891			if ( $nDateTimeStamp > 0 )
892			{
893				if ( strlen($szWhere) > 0 )
894					$szWhere .= " AND ";
895				else
896					$szWhere = " WHERE ";
897
898				// Append Date Filter!
899				$szWhere .= " UNIX_TIMESTAMP(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_DATE] . ") < " . $nDateTimeStamp;
900			}
901			// ---
902
903			// DELETE DATA NOW!
904			$szSql = "DELETE FROM `" .  $this->_logStreamConfigObj->DBTableName . "`" . $szWhere;
905			OutputDebugMessage("LogStreamDB|CleanupLogdataByDate: Created SQL Query:<br>" . $szSql, DEBUG_DEBUG);
906			$myQuery = mysqli_query($this->_dbhandle, $szSql);
907			if ($myQuery)
908			{
909				// Get affected rows and return!
910				$rowcount = mysqli_affected_rows($this->_dbhandle);
911
912				// Reset AUTO_INCREMENT if all records were deleted!
913				if ( $nDateTimeStamp == 0 )
914				{
915					$szSql = "ALTER TABLE " . $this->_logStreamConfigObj->DBTableName . " AUTO_INCREMENT=0";
916					$myQuery = mysqli_query($this->_dbhandle, $szSql);
917					// error occured, output DEBUG message
918					if (!$myQuery)
919						$this->PrintDebugError("CleanupLogdataByDate failed to reset AUTO_INCREMENT for '" . $this->_logStreamConfigObj->DBTableName . "' table. ");
920				}
921
922				// Free result not needed here!
923				//mysqli_free_result ($myQuery);
924			}
925			else
926			{
927				// error occured, output DEBUG message
928				$this->PrintDebugError("CleanupLogdataByDate failed with SQL Statement ' " . $szSql . " '");
929			}
930		}
931
932		//return affected rows
933		return $rowcount;
934	}
935
936
937	/*
938	*	Implementation of the UpdateAllMessageChecksum
939	*
940	*	Update all missing checksum properties in the current database
941	*/
942	public function UpdateAllMessageChecksum( )
943	{
944		global $querycount, $dbmapping;
945		$szTableType = $this->_logStreamConfigObj->DBTableType;
946
947		// UPDATE DATA NOW!
948		$szSql =	"UPDATE " . $this->_logStreamConfigObj->DBTableName .
949					" SET " . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . " = crc32(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_MESSAGE] . ") " .
950					" WHERE " . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . " IS NULL OR " . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . " = 0";
951
952		// Output Debug Informations
953		OutputDebugMessage("LogStreamDB|UpdateAllMessageChecksum: Running Created SQL Query:<br>" . $szSql, DEBUG_ULTRADEBUG);
954
955		// Running SQL Query
956		$myQuery = mysqli_query($this->_dbhandle, $szSql);
957		if ($myQuery)
958		{
959			// Debug Output
960			OutputDebugMessage("LogStreamDB|UpdateAllMessageChecksum: Successfully updated Checksum of '" . mysqli_affected_rows($this->_dbhandle) . "' datarecords", DEBUG_INFO);
961
962			// Return success
963			return SUCCESS;
964		}
965		else
966		{
967			// error occured, output DEBUG message
968			$this->PrintDebugError("SaveMessageChecksum failed with SQL Statement ' " . $szSql . " '");
969
970			// Failed
971			return ERROR;
972		}
973	}
974
975
976	/*
977	*	Implementation of the SaveMessageChecksum
978	*
979	*	Creates an database UPDATE Statement and performs it!
980	*/
981	public function SaveMessageChecksum( $arrProperitesIn )
982	{
983		global $querycount, $dbmapping;
984		$szTableType = $this->_logStreamConfigObj->DBTableType;
985
986		if ( isset($arrProperitesIn[SYSLOG_UID]) && isset($arrProperitesIn[MISC_CHECKSUM]) && isset($dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM]) )
987		{
988			// UPDATE DATA NOW!
989			$szSql =	"UPDATE " . $this->_logStreamConfigObj->DBTableName .
990						" SET " . $dbmapping[$szTableType]['DBMAPPINGS'][MISC_CHECKSUM] . " = " . $arrProperitesIn[MISC_CHECKSUM] .
991						" WHERE " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " = " . $arrProperitesIn[SYSLOG_UID];
992			$myQuery = mysqli_query($this->_dbhandle, $szSql);
993			if ($myQuery)
994			{
995				// Return success
996				return SUCCESS;
997			}
998			else
999			{
1000				// error occured, output DEBUG message
1001				$this->PrintDebugError("SaveMessageChecksum failed with SQL Statement ' " . $szSql . " '");
1002
1003				// Failed
1004				return ERROR;
1005			}
1006		}
1007		else
1008			// Missing important properties
1009			return ERROR;
1010	}
1011
1012
1013	/**
1014	* Implementation of ConsolidateItemListByField
1015	*
1016	* In the native MYSQL Logstream, the database will do most of the work
1017	*
1018	* @return integer Error stat
1019	*/
1020	public function ConsolidateItemListByField($szConsFieldId, $nRecordLimit, $szSortFieldId, $nSortingOrder)
1021	{
1022		global $content, $dbmapping, $fields;
1023
1024		// Copy helper variables, this is just for better readability
1025		$szTableType = $this->_logStreamConfigObj->DBTableType;
1026
1027		// Check if fields are available
1028		if ( !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId]) || !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szSortFieldId]) )
1029			return ERROR_DB_DBFIELDNOTFOUND;
1030
1031		// --- Set Options
1032		$nConsFieldType = $fields[$szConsFieldId]['FieldType'];
1033
1034		if ( $nSortingOrder == SORTING_ORDER_DESC )
1035			$szSortingOrder = "DESC";
1036		else
1037			$szSortingOrder = "ASC";
1038		// ---
1039
1040		// --- Set DB Field names
1041		$myDBConsFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId];
1042		$myDBGroupByFieldName = $myDBConsFieldName;
1043		$myDBQueryFields = $myDBConsFieldName . ", ";
1044
1045		// Set Sorted Field
1046		if ( $szConsFieldId == $szSortFieldId )
1047			$myDBSortedFieldName = "itemcount";
1048		else
1049			$myDBSortedFieldName = $szSortFieldId;
1050		// ---
1051
1052		// Special handling for date fields
1053		if ( $nConsFieldType == FILTER_TYPE_DATE )
1054		{
1055			// Helper variable for the select statement
1056			$mySelectFieldName = $myDBGroupByFieldName . "grouped";
1057			$myDBQueryFieldName = "DATE( " . $myDBConsFieldName . ") AS " . $myDBGroupByFieldName ;
1058		}
1059
1060		// Set Limit String
1061		if ( $nRecordLimit > 0 )
1062			$szLimitSql = " LIMIT " . $nRecordLimit;
1063		else
1064			$szLimitSql = "";
1065
1066		// Create SQL Where Clause!
1067		if ( $this->_SQLwhereClause == "" )
1068		{
1069			$res = $this->CreateSQLWhereClause();
1070			if ( $res != SUCCESS )
1071				return $res;
1072		}
1073
1074		// Create SQL String now!
1075		$szSql =	"SELECT " .
1076					$myDBQueryFields .
1077					"count(" . $myDBConsFieldName . ") as itemcount " .
1078					" FROM `" . $this->_logStreamConfigObj->DBTableName . "`" .
1079					$this->_SQLwhereClause .
1080					" GROUP BY " . $myDBGroupByFieldName .
1081					" ORDER BY " . $myDBSortedFieldName . " " . $szSortingOrder .
1082					$szLimitSql ;
1083
1084		// Output Debug Informations
1085		OutputDebugMessage("LogStreamDB|ConsolidateItemListByField: Running Created SQL Query:<br>" . $szSql, DEBUG_ULTRADEBUG);
1086
1087		// Perform Database Query
1088		$myquery = mysqli_query($this->_dbhandle, $szSql);
1089		if ( !$myquery ) {
1090			$this->PrintDebugError("Invalid SQL: ".$szSql);
1091			return ERROR_DB_QUERYFAILED;
1092		}
1093
1094		// Initialize Array variable
1095		$aResult = array();
1096
1097		// read data records
1098		while ($myRow = mysqli_fetch_array($myquery,  MYSQLI_ASSOC))
1099		{
1100			// Keys need to be converted into lowercase!
1101			$myRow = array_change_key_case($myRow, CASE_LOWER);
1102
1103			// Create new row
1104			$aNewRow = array();
1105
1106			foreach ( $myRow as $myFieldName => $myFieldValue )
1107			{
1108				if ( $myFieldName == $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId] )
1109					$aNewRow[$szConsFieldId] = $myFieldValue;
1110				else
1111					$aNewRow[$myFieldName] = $myFieldValue;
1112			}
1113
1114			// Add new row to result
1115			$aResult[] = $aNewRow;
1116		}
1117
1118		// return finished array
1119		if ( count($aResult) > 0 )
1120			return $aResult;
1121		else
1122			return ERROR_NOMORERECORDS;
1123	}
1124
1125
1126	/**
1127	* Implementation of ConsolidateDataByField
1128	*
1129	* In the native MYSQL Logstream, the database will do most of the work
1130	*
1131	* @return integer Error stat
1132	*/
1133	public function ConsolidateDataByField($szConsFieldId, $nRecordLimit, $szSortFieldId, $nSortingOrder, $aIncludeCustomFields = null, $bIncludeLogStreamFields = false, $bIncludeMinMaxDateFields = false)
1134	{
1135		global $content, $dbmapping, $fields;
1136
1137		// Copy helper variables, this is just for better readability
1138		$szTableType = $this->_logStreamConfigObj->DBTableType;
1139
1140		// Check if fields are available
1141		if ( !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId]) || !isset($dbmapping[$szTableType]['DBMAPPINGS'][$szSortFieldId]) )
1142			return ERROR_DB_DBFIELDNOTFOUND;
1143
1144		// --- Set Options
1145		$nConsFieldType = $fields[$szConsFieldId]['FieldType'];
1146
1147		if ( $nSortingOrder == SORTING_ORDER_DESC )
1148			$szSortingOrder = "DESC";
1149		else
1150			$szSortingOrder = "ASC";
1151		// ---
1152
1153		// --- Set DB Field names
1154		$myDBConsFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szConsFieldId];
1155		$myDBGroupByFieldName = $myDBConsFieldName;
1156
1157		// Check which fields to include
1158		if ( $aIncludeCustomFields != null )
1159		{
1160			$myDBQueryFields = "";
1161			foreach ( $aIncludeCustomFields as $myFieldName )
1162			{
1163				if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName]) )
1164					$myDBQueryFields .= $dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName] . ", ";
1165			}
1166
1167			// Append Sortingfield
1168			if ( !in_array($szConsFieldId, $aIncludeCustomFields) )
1169				$myDBQueryFields .= $myDBConsFieldName . ", ";
1170		}
1171		else if ( $bIncludeLogStreamFields )
1172		{
1173			$myDBQueryFields = "";
1174			foreach ( $this->_arrProperties as $myFieldName )
1175			{
1176				if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName]) )
1177					$myDBQueryFields .= $dbmapping[$szTableType]['DBMAPPINGS'][$myFieldName] . ", ";
1178			}
1179		}
1180		else // Only Include ConsolidateField
1181			$myDBQueryFields = $myDBConsFieldName . ", ";
1182
1183		// Add Min and Max fields for DATE if desired
1184		if ( $bIncludeMinMaxDateFields )
1185		{
1186			$myDBQueryFields .= "Min(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_DATE] . ") as firstoccurrence_date, ";
1187			$myDBQueryFields .= "Max(" . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_DATE] . ") as lastoccurrence_date, ";
1188		}
1189
1190		if ( $szConsFieldId == $szSortFieldId )
1191			$myDBSortedFieldName = "itemcount";
1192		else
1193			$myDBSortedFieldName = $szSortFieldId;
1194		// ---
1195
1196		// Special handling for date fields
1197		if ( $nConsFieldType == FILTER_TYPE_DATE )
1198		{
1199			// Helper variable for the select statement
1200			$mySelectFieldName = $myDBGroupByFieldName . "grouped";
1201			$myDBQueryFieldName = "DATE( " . $myDBConsFieldName . ") AS " . $myDBGroupByFieldName ;
1202		}
1203
1204		// Set Limit String
1205		if ( $nRecordLimit > 0 )
1206			$szLimitSql = " LIMIT " . $nRecordLimit;
1207		else
1208			$szLimitSql = "";
1209
1210		// Create SQL Where Clause!
1211		if ( strlen($this->_SQLwhereClause) <= 0 )
1212		{
1213			$res = $this->CreateSQLWhereClause();
1214			if ( $res != SUCCESS )
1215				return $res;
1216		}
1217
1218		// Create SQL String now!
1219		$szSql =	"SELECT " .
1220					$myDBQueryFields .
1221					"count(" . $myDBConsFieldName . ") as itemcount " .
1222					" FROM `" . $this->_logStreamConfigObj->DBTableName . "`" .
1223					$this->_SQLwhereClause .
1224					" GROUP BY " . $myDBGroupByFieldName .
1225					" ORDER BY " . $myDBSortedFieldName . " " . $szSortingOrder .
1226					$szLimitSql ;
1227
1228		// Output Debug Informations
1229		OutputDebugMessage("LogStreamDB|ConsolidateDataByField: Running Created SQL Query:<br>" . $szSql, DEBUG_ULTRADEBUG);
1230
1231		// Perform Database Query
1232		$myquery = mysqli_query($this->_dbhandle, $szSql);
1233		if ( !$myquery ) {
1234			$this->PrintDebugError("Invalid SQL: ".$szSql);
1235			return ERROR_DB_QUERYFAILED;
1236		}
1237
1238		// Initialize Array variable
1239		$aResult = array();
1240
1241		// read data records
1242		while ($myRow = mysqli_fetch_array($myquery,  MYSQLI_ASSOC))
1243		{
1244			// Keys need to be converted into lowercase!
1245			$myRow = array_change_key_case($myRow, CASE_LOWER);
1246
1247			// Create new row
1248			$aNewRow = array();
1249
1250			foreach ( $myRow as $myFieldName => $myFieldValue )
1251			{
1252				$myFieldID = $this->GetFieldIDbyDatabaseMapping($szTableType, $myFieldName);
1253				$aNewRow[ $myFieldID ] = $myFieldValue;
1254			}
1255			// Add new row to result
1256			$aResult[] = $aNewRow;
1257		}
1258
1259		// return finished array
1260		if ( count($aResult) > 0 )
1261			return $aResult;
1262		else
1263			return ERROR_NOMORERECORDS;
1264	}
1265
1266	/**
1267	* Implementation of GetCountSortedByField
1268	*
1269	* In the native MYSQL Logstream, the database will do most of the work
1270	*
1271	* @return integer Error stat
1272	*/
1273	public function GetCountSortedByField($szFieldId, $nFieldType, $nRecordLimit)
1274	{
1275		global $content, $dbmapping;
1276
1277		// Copy helper variables, this is just for better readability
1278		$szTableType = $this->_logStreamConfigObj->DBTableType;
1279
1280		if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$szFieldId]) )
1281		{
1282			// Set DB Field name first!
1283			$myDBFieldName = $dbmapping[$szTableType]['DBMAPPINGS'][$szFieldId];
1284			$myDBQueryFieldName = $myDBFieldName;
1285			$mySelectFieldName = $myDBFieldName;
1286
1287			// Special handling for date fields
1288			if ( $nFieldType == FILTER_TYPE_DATE )
1289			{
1290				// Helper variable for the select statement
1291				$mySelectFieldName = $mySelectFieldName . "grouped";
1292				$myDBQueryFieldName = "DATE( " . $myDBFieldName . ") AS " . $mySelectFieldName ;
1293			}
1294
1295			// Create SQL Where Clause!
1296			if ( $this->_SQLwhereClause == "" )
1297			{
1298				$res = $this->CreateSQLWhereClause();
1299				if ( $res != SUCCESS )
1300					return $res;
1301			}
1302
1303			// Create SQL String now!
1304			$szSql =	"SELECT " .
1305						$myDBQueryFieldName . ", " .
1306						"count(" . $myDBFieldName . ") as totalcount " .
1307						" FROM `" . $this->_logStreamConfigObj->DBTableName . "`" .
1308						$this->_SQLwhereClause .
1309						" GROUP BY " . $mySelectFieldName .
1310						" ORDER BY totalcount DESC" .
1311						" LIMIT " . $nRecordLimit;
1312
1313			// Perform Database Query
1314			$myquery = mysqli_query($this->_dbhandle, $szSql);
1315			if ( !$myquery ) {
1316				$this->PrintDebugError("Invalid SQL: ".$szSql);
1317				return ERROR_DB_QUERYFAILED;
1318			}
1319
1320			// Initialize Array variable
1321			$aResult = array();
1322
1323			// read data records
1324			while ($myRow = mysqli_fetch_array($myquery,  MYSQLI_ASSOC))
1325			{
1326				// Keys need to be converted into lowercase!
1327				$myRow = array_change_key_case($myRow, CASE_LOWER);
1328
1329				// Set totalcount Result
1330				$aResult[ $myRow[$mySelectFieldName] ] = $myRow['totalcount'];
1331			}
1332
1333			// return finished array
1334			if ( count($aResult) > 0 )
1335				return $aResult;
1336			else
1337				return ERROR_NOMORERECORDS;
1338		}
1339		else
1340		{
1341			// return error code, field mapping not found
1342			return ERROR_DB_DBFIELDNOTFOUND;
1343		}
1344	}
1345
1346
1347
1348	/*
1349	*	============= Beginn of private functions =============
1350	*/
1351
1352	/*
1353	*	This function expects the filters to already being set earlier.
1354	*	Otherwise no usual WHERE Clause can be created!
1355	*/
1356	private function CreateSQLWhereClause()
1357	{
1358		if ( $this->_filters != null )
1359		{
1360			global $dbmapping;
1361			$szTableType = $this->_logStreamConfigObj->DBTableType;
1362
1363			// Reset WhereClause
1364			$this->_SQLwhereClause = "";
1365
1366			// --- Build Query Array
1367			$arrayQueryProperties = $this->_arrProperties;
1368			if ( isset($this->_arrFilterProperties) && $this->_arrFilterProperties != null)
1369			{
1370				foreach ( $this->_arrFilterProperties as $filterproperty )
1371				{
1372					if ( $this->_arrProperties == null || !in_array($filterproperty, $this->_arrProperties) )
1373						$arrayQueryProperties[] = $filterproperty;
1374				}
1375			}
1376			// ---
1377
1378			// Loop through all available properties
1379			foreach( $arrayQueryProperties as $propertyname )
1380			{
1381				// If the property exists in the filter array, we have something to filter for ^^!
1382				if ( array_key_exists($propertyname, $this->_filters) )
1383				{
1384					// Process all filters
1385					foreach( $this->_filters[$propertyname] as $myfilter )
1386					{
1387						// Only perform if database mapping is available for this filter!
1388						if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$propertyname]) )
1389						{
1390							switch( $myfilter[FILTER_TYPE] )
1391							{
1392								case FILTER_TYPE_STRING:
1393									// --- Either make a LIKE or a equal query!
1394									if ( $myfilter[FILTER_MODE] & FILTER_MODE_SEARCHFULL )
1395									{
1396										// Set addnot to nothing
1397										$addnod = "";
1398
1399										// --- Check if user wants to include or exclude!
1400										if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
1401										{
1402											$szSearchBegin = " = '";
1403											$szSearchEnd = "' ";
1404										}
1405										else
1406										{
1407											$szSearchBegin = " <> '";
1408											$szSearchEnd = "' ";
1409										}
1410										// ---
1411									}
1412									else if ( $myfilter[FILTER_MODE] & FILTER_MODE_SEARCHREGEX )
1413									{
1414										// --- Check if user wants to include or exclude!
1415										if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
1416											$addnod = "";
1417										else
1418											$addnod = " NOT";
1419										// ---
1420
1421										$szSearchBegin = " REGEXP '";
1422										$szSearchEnd = "' ";
1423									}
1424									else
1425									{
1426										// --- Check if user wants to include or exclude!
1427										if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
1428											$addnod = "";
1429										else
1430											$addnod = " NOT";
1431										// ---
1432
1433										$szSearchBegin = " LIKE '%";
1434										$szSearchEnd = "%' ";
1435									}
1436									// ---
1437
1438									// --- If Syslog message, we have AND handling, otherwise OR!
1439									if ( $propertyname == SYSLOG_MESSAGE )
1440										$addor = " AND ";
1441									else
1442									{
1443										// If we exclude filters, we need to combine with AND
1444										if ( $myfilter[FILTER_MODE] & FILTER_MODE_INCLUDE)
1445											$addor = " OR ";
1446										else
1447											$addor = " AND ";
1448									}
1449									// ---
1450
1451									// Now Create LIKE Filters
1452									if ( isset($tmpfilters[$propertyname]) )
1453										$tmpfilters[$propertyname][FILTER_VALUE] .= $addor . $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . $addnod . $szSearchBegin . DB_RemoveBadChars($myfilter[FILTER_VALUE]) . $szSearchEnd;
1454									else
1455									{
1456										$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_STRING;
1457										$tmpfilters[$propertyname][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . $addnod . $szSearchBegin . DB_RemoveBadChars($myfilter[FILTER_VALUE]) . $szSearchEnd;
1458									}
1459									break;
1460								case FILTER_TYPE_NUMBER:
1461									// --- Check if user wants to include or exclude!
1462									if ( $myfilter[FILTER_MODE] & FILTER_MODE_EXCLUDE )
1463									{
1464										// Add to filterset
1465										$szArrayKey = $propertyname . "-NOT";
1466										if ( isset($tmpfilters[$szArrayKey]) )
1467											$tmpfilters[$szArrayKey][FILTER_VALUE] .= ", " . $myfilter[FILTER_VALUE];
1468										else
1469										{
1470											$tmpfilters[$szArrayKey][FILTER_TYPE] = FILTER_TYPE_NUMBER;
1471											$tmpfilters[$szArrayKey][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " NOT IN (" . DB_RemoveBadChars($myfilter[FILTER_VALUE]);
1472										}
1473									}
1474									else
1475									{
1476										// Add to filterset
1477										if ( isset($tmpfilters[$propertyname]) )
1478											$tmpfilters[$propertyname][FILTER_VALUE] .= ", " . $myfilter[FILTER_VALUE];
1479										else
1480										{
1481											$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_NUMBER;
1482											$tmpfilters[$propertyname][FILTER_VALUE] = $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " IN (" . DB_RemoveBadChars($myfilter[FILTER_VALUE]);
1483										}
1484									}
1485									// ---
1486									break;
1487								case FILTER_TYPE_DATE:
1488									if ( isset($tmpfilters[$propertyname]) )
1489										$tmpfilters[$propertyname][FILTER_VALUE] .= " AND ";
1490									else
1491									{
1492										$tmpfilters[$propertyname][FILTER_VALUE] = "";
1493										$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_DATE;
1494									}
1495
1496									if ( $myfilter[FILTER_DATEMODE] == DATEMODE_LASTX )
1497									{
1498										// Get current timestamp
1499										$nNowTimeStamp = time();
1500
1501										if		( $myfilter[FILTER_VALUE] == DATE_LASTX_HOUR )
1502											$nNowTimeStamp -= 60 * 60; // One Hour!
1503										else if	( $myfilter[FILTER_VALUE] == DATE_LASTX_12HOURS )
1504											$nNowTimeStamp -= 60 * 60 * 12; // 12 Hours!
1505										else if	( $myfilter[FILTER_VALUE] == DATE_LASTX_24HOURS )
1506											$nNowTimeStamp -= 60 * 60 * 24; // 24 Hours!
1507										else if	( $myfilter[FILTER_VALUE] == DATE_LASTX_7DAYS )
1508											$nNowTimeStamp -= 60 * 60 * 24 * 7; // 7 days
1509										else if	( $myfilter[FILTER_VALUE] == DATE_LASTX_31DAYS )
1510											$nNowTimeStamp -= 60 * 60 * 24 * 31; // 31 days
1511										else
1512										{
1513											// Set filter to unknown and Abort in this case!
1514											$tmpfilters[$propertyname][FILTER_TYPE] = FILTER_TYPE_UNKNOWN;
1515											break;
1516										}
1517
1518										// Append filter
1519										$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " > '" . date("Y-m-d H:i:s", $nNowTimeStamp) . "'";
1520									}
1521									else if ( $myfilter[FILTER_DATEMODE] == DATEMODE_RANGE_FROM )
1522									{
1523										// Obtain Event struct for the time!
1524										$myeventtime = GetEventTime($myfilter[FILTER_VALUE]);
1525										$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " > '" . date("Y-m-d H:i:s", $myeventtime[EVTIME_TIMESTAMP]) . "'";
1526									}
1527									else if ( $myfilter[FILTER_DATEMODE] == DATEMODE_RANGE_TO )
1528									{
1529										// Obtain Event struct for the time!
1530										$myeventtime = GetEventTime($myfilter[FILTER_VALUE]);
1531										$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " < '" . date("Y-m-d H:i:s", $myeventtime[EVTIME_TIMESTAMP]) . "'";
1532									}
1533									else if ( $myfilter[FILTER_DATEMODE] == DATEMODE_RANGE_DATE )
1534									{
1535										// Obtain Event struct for the time!
1536										$myeventtime = GetEventTime($myfilter[FILTER_VALUE]);
1537										$tmpfilters[$propertyname][FILTER_VALUE] .= $dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " > '" . date("Y-m-d H:i:s", $myeventtime[EVTIME_TIMESTAMP]) . "' AND " .
1538																					$dbmapping[$szTableType]['DBMAPPINGS'][$propertyname] . " < '" . date("Y-m-d H:i:s", ($myeventtime[EVTIME_TIMESTAMP]+86400) ) . "'";
1539									}
1540
1541									break;
1542								default:
1543									// Nothing to do!
1544									break;
1545							}
1546						}
1547						else
1548						{
1549							// Check how to treat not found db mappings / filters
1550							if ( GetConfigSetting("TreatNotFoundFiltersAsTrue", 0, CFGLEVEL_USER) == 0 )
1551								return ERROR_DB_DBFIELDNOTFOUND;
1552						}
1553					}
1554				}
1555			}
1556
1557			// Check and combine all filters now!
1558			if ( isset($tmpfilters) )
1559			{
1560				// Append filters
1561				foreach( $tmpfilters as $tmpfilter )
1562				{
1563					// Init WHERE or Append AND
1564					if ( strlen($this->_SQLwhereClause) > 0 )
1565						$this->_SQLwhereClause .= " AND ";
1566					else
1567						$this->_SQLwhereClause = " WHERE ";
1568
1569					switch( $tmpfilter[FILTER_TYPE] )
1570					{
1571						case FILTER_TYPE_STRING:
1572							$this->_SQLwhereClause .= "( " . $tmpfilter[FILTER_VALUE] . ") ";
1573							break;
1574						case FILTER_TYPE_NUMBER:
1575							$this->_SQLwhereClause .= $tmpfilter[FILTER_VALUE] . ") ";
1576							break;
1577						case FILTER_TYPE_DATE:
1578							$this->_SQLwhereClause .= $tmpfilter[FILTER_VALUE];
1579							break;
1580						default:
1581							// Should not happen, wrong filters!
1582							// We add a dummy into the where clause, just as a place holder
1583							$this->_SQLwhereClause .= " 1=1 ";
1584							break;
1585					}
1586				}
1587			}
1588
1589// echo $this->_SQLwhereClause;
1590			//$dbmapping[$szTableType][SYSLOG_UID]
1591		}
1592		else // No filters means nothing to do!
1593			return SUCCESS;
1594	}
1595
1596	/*
1597	*	Destroy the SQL QUery!
1598	*/
1599	private function DestroyMainSQLQuery()
1600	{
1601		// create query if necessary!
1602		if ( $this->_myDBQuery != null )
1603		{
1604			// Free Query ressources
1605			mysqli_free_result ($this->_myDBQuery);
1606			$this->_myDBQuery = null;
1607		}
1608
1609		// return success state if reached this point!
1610		return SUCCESS;
1611	}
1612
1613	/*
1614	*	This helper function will read the next records into the buffer.
1615	*/
1616	private function ReadNextRecordsFromDB($uID)
1617	{
1618		global $querycount;
1619
1620		// Clear SQL Query first!
1621		$this->DestroyMainSQLQuery();
1622
1623		// return error if there was one!
1624		if ( ($res = $this->CreateMainSQLQuery($uID)) != SUCCESS )
1625			return $res;
1626
1627		// Append LIMIT clause
1628//		$szSql .= " LIMIT " . $this->_currentRecordStart . ", " . $this->_logStreamConfigObj->RecordsPerQuery;
1629
1630		// Copy rows into the buffer!
1631		$iBegin = $this->_currentRecordNum;
1632		while ($myRow = mysqli_fetch_array($this->_myDBQuery,  MYSQLI_ASSOC))
1633		{
1634			// Check if result was successfull!
1635			if ( $myRow === FALSE || !$myRow  )
1636				break;
1637
1638			// Keys need to be converted into lowercase!
1639			$this->bufferedRecords[$iBegin] = array_change_key_case($myRow, CASE_LOWER);
1640			$iBegin++;
1641		}
1642
1643		// --- Check if results were found
1644		if ( $iBegin == $this->_currentRecordNum )
1645			return ERROR_NOMORERECORDS;
1646		// ---
1647
1648		// Free Query ressources
1649//		mysqli_free_result ($myquery);
1650
1651		// Only obtain count if enabled and not done before
1652		if ( $this->_logStreamConfigObj->DBEnableRowCounting && $this->_totalRecordCount == -1 )
1653		{
1654			$this->_totalRecordCount = $this->GetRowCountFromTable();
1655
1656			if ( $this->_totalRecordCount <= 0 )
1657				return ERROR_NOMORERECORDS;
1658		}
1659
1660		// Increment for the Footer Stats
1661		$querycount++;
1662
1663		// return success state if reached this point!
1664		return SUCCESS;
1665	}
1666
1667	/*
1668	*	Create the SQL QUery!
1669	*/
1670	private function CreateMainSQLQuery($uID)
1671	{
1672		global $querycount;
1673
1674		// Get SQL Statement
1675		$szSql = $this->CreateSQLStatement($uID);
1676
1677		// --- Append LIMIT
1678		$szSql .= " LIMIT " . $this->_logStreamConfigObj->RecordsPerQuery;
1679		// ---
1680
1681		// Perform Database Query
1682		$this->_myDBQuery = mysqli_query($this->_dbhandle, $szSql);
1683		if ( !$this->_myDBQuery )
1684		{
1685			// Check if a field is missing!
1686			if ( mysqli_errno($this->_dbhandle) == 1054 )
1687			{
1688				// Handle missing field and try again!
1689				if ( $this->HandleMissingField() == SUCCESS )
1690				{
1691					$this->_myDBQuery = mysqli_query($this->_dbhandle, $szSql);
1692					if ( !$this->_myDBQuery ) {
1693						$this->PrintDebugError("Invalid SQL: ".$szSql);
1694						return ERROR_DB_QUERYFAILED;
1695					}
1696				}
1697				else // Failed to add field dynamically
1698					return ERROR_DB_QUERYFAILED;
1699			}
1700			else
1701			{
1702				$this->PrintDebugError("Invalid SQL: ".$szSql);
1703				return ERROR_DB_QUERYFAILED;
1704			}
1705		}
1706		else
1707		{
1708			// Skip one entry in this case
1709			if ( $this->_currentRecordStart > 0 )
1710			{
1711				// Throw away
1712				$myRow = mysqli_fetch_array($this->_myDBQuery,  MYSQLI_ASSOC);
1713			}
1714		}
1715
1716		// Increment for the Footer Stats
1717		$querycount++;
1718
1719		// Output Debug Informations
1720		OutputDebugMessage("LogStreamDB|CreateMainSQLQuery: Created SQL Query:<br>" . $szSql, DEBUG_DEBUG);
1721
1722		// return success state if reached this point!
1723		return SUCCESS;
1724	}
1725
1726	/*
1727	*	Creates the SQL Statement we are going to use!
1728	*/
1729	private function CreateSQLStatement($uID, $includeFields = true)
1730	{
1731		global $dbmapping;
1732
1733		// Copy helper variables, this is just for better readability
1734		$szTableType = $this->_logStreamConfigObj->DBTableType;
1735		$szSortColumn = $this->_logStreamConfigObj->SortColumn;
1736
1737		// Create Basic SQL String
1738		if ( $this->_logStreamConfigObj->DBEnableRowCounting ) // with SQL_CALC_FOUND_ROWS
1739			$sqlString = "SELECT SQL_CALC_FOUND_ROWS " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID];
1740		else													// without row calc
1741			$sqlString = "SELECT " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID];
1742
1743		// Append fields if needed
1744		if ( $includeFields && $this->_arrProperties != null )
1745		{
1746			// Loop through all requested fields
1747			foreach ( $this->_arrProperties as $myproperty )
1748			{
1749				// SYSLOG_UID already added!
1750				if ( $myproperty != SYSLOG_UID && isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) )
1751				{
1752					// Append field!
1753					$sqlString .= ", " . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty];
1754				}
1755			}
1756		}
1757
1758		// Append FROM 'table'!
1759		$sqlString .= " FROM `" . $this->_logStreamConfigObj->DBTableName . "`";
1760
1761		// Append precreated where clause
1762		$sqlString .= $this->_SQLwhereClause;
1763
1764		// Append UID QUERY!
1765		if ( $uID != -1 )
1766		{
1767			if ( $this->_readDirection == EnumReadDirection::Forward )
1768				$myOperator = ">=";
1769			else
1770				$myOperator = "<=";
1771
1772			if ( strlen($this->_SQLwhereClause) > 0 )
1773				$sqlString .= " AND " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " $myOperator $uID";
1774			else
1775				$sqlString .= " WHERE " . $dbmapping[$szTableType]['DBMAPPINGS'][SYSLOG_UID] . " $myOperator $uID";
1776		}
1777
1778		// Append ORDER clause
1779		if ( $this->_readDirection == EnumReadDirection::Forward )
1780			$sqlString .= " ORDER BY " .  $dbmapping[$szTableType]['DBMAPPINGS'][$szSortColumn];
1781		else if ( $this->_readDirection == EnumReadDirection::Backward )
1782			$sqlString .= " ORDER BY " .  $dbmapping[$szTableType]['DBMAPPINGS'][$szSortColumn] . " DESC";
1783
1784		// return SQL result string:
1785		return $sqlString;
1786	}
1787
1788	/*
1789	*	Reset record buffer in this function!
1790	*/
1791	private function ResetBufferedRecords()
1792	{
1793		if ( isset($this->bufferedRecords) )
1794		{
1795			// Loop through all subrecords first!
1796			foreach ($this->bufferedRecords as $mykey => $myrecord)
1797				unset( $this->bufferedRecords[$mykey] );
1798
1799			// Set buffered records to NULL!
1800			$this->bufferedRecords = null;
1801		}
1802	}
1803
1804	/*
1805	*	Helper function to display SQL Errors for now!
1806	*/
1807	private function PrintDebugError($szErrorMsg)
1808	{
1809		global $extraErrorDescription;
1810
1811		$errdesc = mysqli_error($this->_dbhandle);
1812		$errno = mysqli_errno($this->_dbhandle);
1813
1814		$errormsg="$szErrorMsg <br>";
1815		$errormsg.="Detail error: $errdesc <br>";
1816		$errormsg.="Error Code: $errno <br>";
1817
1818		// Add to additional error output
1819		$extraErrorDescription = $errormsg;
1820
1821		//Output!
1822		OutputDebugMessage("LogStreamDB|PrintDebugError: $errormsg", DEBUG_ERROR);
1823	}
1824
1825	/*
1826	*	Returns the number of possible records by using a query
1827	*/
1828	private function GetRowCountByString($szQuery)
1829	{
1830		if ($myQuery = mysqli_query($this->_dbhandle, $szQuery))
1831		{
1832			$num_rows = mysqli_num_rows($myQuery);
1833			mysqli_free_result ($myQuery);
1834		}
1835		return $num_rows;
1836	}
1837
1838	/*
1839	*	Returns the number of possible records by using an existing queryid
1840	*/
1841	private function GetRowCountByQueryID($myQuery)
1842	{
1843		$num_rows = mysqli_num_rows($myQuery);
1844		return $num_rows;
1845	}
1846
1847	/*
1848	*	Returns the number of possible records by using a select count statement!
1849	*/
1850	private function GetRowCountFromTable()
1851	{
1852		if ( $myquery = mysqli_query($this->_dbhandle, "Select FOUND_ROWS();") )
1853		{
1854			// Get first and only row!
1855			$myRow = mysqli_fetch_array($myquery);
1856
1857			// copy row count
1858			$numRows = $myRow[0];
1859		}
1860		else
1861			$numRows = -1;
1862
1863		// return result!
1864		return $numRows;
1865	}
1866
1867	/*
1868	*	Function handles missing database fields automatically!
1869	*/
1870	private function HandleMissingField( $szMissingField = null, $arrProperties = null )
1871	{
1872		global $dbmapping, $fields;
1873
1874		// Get Err description
1875		$errdesc = mysqli_error($this->_dbhandle);
1876
1877		// Try to get missing field from SQL Error of not specified as argument
1878		if ( $szMissingField == null )
1879		{
1880			if ( preg_match("/Unknown column '(.*?)' in '(.*?)'$/", $errdesc, $errOutArr ) )
1881				$szMissingField = $errOutArr[1];
1882			else
1883			{
1884				$this->PrintDebugError("ER_BAD_FIELD_ERROR - SQL Statement: ". $errdesc);
1885				return ERROR_DB_DBFIELDNOTFOUND;
1886			}
1887		}
1888
1889		// Set Properties to default if NULL
1890		if ( $arrProperties == null )
1891			$arrProperties = $this->_arrProperties;
1892
1893		// Get Tabletype
1894		$szTableType = $this->_logStreamConfigObj->DBTableType;
1895
1896		// Loop through all fields to see which one is missing!
1897		foreach ( $arrProperties as $myproperty )
1898		{
1899			if ( isset($dbmapping[$szTableType]['DBMAPPINGS'][$myproperty]) && $szMissingField == $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] )
1900			{
1901				// Create SQL Numeric field
1902				$szUpdateSql = ""; $szUnsigned = "";
1903				if ( $fields[$myproperty]['FieldType'] == FILTER_TYPE_NUMBER )
1904				{
1905					// This will add the checksum field as unsigned automatically!
1906					if ( $myproperty == MISC_CHECKSUM )
1907						$szUnsigned = "UNSIGNED";
1908					$szUpdateSql = "ALTER TABLE `" . $this->_logStreamConfigObj->DBTableName . "` ADD `" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "` int(11) " . $szUnsigned . " NOT NULL DEFAULT '0'";
1909				}
1910				if ( $fields[$myproperty]['FieldType'] == FILTER_TYPE_STRING )
1911					$szUpdateSql = "ALTER TABLE `" . $this->_logStreamConfigObj->DBTableName . "` ADD `" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "` varchar(60) NOT NULL DEFAULT ''";
1912				if ( $fields[$myproperty]['FieldType'] == FILTER_TYPE_DATE )
1913					$szUpdateSql = "ALTER TABLE `" . $this->_logStreamConfigObj->DBTableName . "` ADD `" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "` datetime NOT NULL DEFAULT '0000-00-00 00:00:00'";
1914
1915				if ( strlen($szUpdateSql) > 0 )
1916				{
1917					// Update Table schema now!
1918					$myQuery = mysqli_query($this->_dbhandle, $szUpdateSql);
1919					if (!$myQuery)
1920					{
1921						// Return failure!
1922						$this->PrintDebugError("ER_BAD_FIELD_ERROR - Dynamically Adding field '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "' with Statement failed: '" . $szUpdateSql . "'");
1923						return ERROR_DB_ADDDBFIELDFAILED;
1924					}
1925				}
1926				else
1927				{
1928					// Return failure!
1929					$this->PrintDebugError("ER_BAD_FIELD_ERROR - Field '" . $dbmapping[$szTableType]['DBMAPPINGS'][$myproperty] . "' is missing has to be added manually to the database layout!'");
1930					return ERROR_DB_ADDDBFIELDFAILED;
1931				}
1932			}
1933		}
1934
1935		// Reached this point means success!
1936		return SUCCESS;
1937	}
1938
1939	/*
1940	*	Helper function to return a list of Indexes for the logstream table
1941	*/
1942	private function GetIndexesAsArray()
1943	{
1944		global $querycount;
1945
1946		// Verify database connection (This also opens the database!)
1947		$res = $this->Verify();
1948		if ( $res != SUCCESS )
1949			return $res;
1950
1951		// Init Array
1952		$arrIndexKeys = array();
1953
1954		// Create SQL and Get INDEXES for table!
1955		$szSql = "SHOW INDEX FROM `" .  $this->_logStreamConfigObj->DBTableName . "`";
1956		$myQuery = mysqli_query($this->_dbhandle, $szSql);
1957		if ($myQuery)
1958		{
1959			// Loop through results
1960			while ($myRow = mysqli_fetch_array($myQuery,  MYSQLI_ASSOC))
1961			{
1962				// Add to index keys
1963				$arrIndexKeys[] = strtolower($myRow['Key_name']);
1964			}
1965
1966			// Free query now
1967			mysqli_free_result ($myQuery);
1968
1969			// Increment for the Footer Stats
1970			$querycount++;
1971		}
1972
1973		// return Array
1974		return $arrIndexKeys;
1975	}
1976
1977
1978	/*
1979	*	Helper function to return a list of Fields from the logstream table
1980	*/
1981	private function GetFieldsAsArray()
1982	{
1983		global $querycount;
1984
1985		// Verify database connection (This also opens the database!)
1986		$res = $this->Verify();
1987		if ( $res != SUCCESS )
1988			return $res;
1989
1990		// Init Array
1991		$arrFieldKeys = array();
1992
1993		// Create SQL and Get INDEXES for table!
1994		$szSql = "SHOW FIELDS FROM `" .  $this->_logStreamConfigObj->DBTableName . "`";
1995		$myQuery = mysqli_query($this->_dbhandle, $szSql);
1996		if ($myQuery)
1997		{
1998			// Loop through results
1999			while ($myRow = mysqli_fetch_array($myQuery,  MYSQLI_ASSOC))
2000			{
2001				// Add to index keys
2002				$arrFieldKeys[] = strtolower($myRow['Field']);
2003			}
2004
2005			// Free query now
2006			mysqli_free_result ($myQuery);
2007
2008			// Increment for the Footer Stats
2009			$querycount++;
2010		}
2011
2012		// return Array
2013		return $arrFieldKeys;
2014	}
2015
2016
2017	/*
2018	*	Helper function to return a list of Indexes for the logstream table
2019	*/
2020	private function GetTriggersAsArray()
2021	{
2022		global $querycount;
2023
2024		// Verify database connection (This also opens the database!)
2025		$res = $this->Verify();
2026		if ( $res != SUCCESS )
2027			return $res;
2028
2029		// Init Array
2030		$arrIndexTriggers = array();
2031
2032		// Create SQL and Get INDEXES for table!
2033		$szSql = "SHOW TRIGGERS";
2034		$myQuery = mysqli_query($this->_dbhandle, $szSql);
2035		if ($myQuery)
2036		{
2037			// Loop through results
2038			while ($myRow = mysqli_fetch_array($myQuery,  MYSQLI_ASSOC))
2039			{
2040//				print_r (  $myRow );
2041				// Add to index keys
2042				$arrIndexTriggers[] = strtolower($myRow['Trigger']);
2043			}
2044
2045			// Free query now
2046			mysqli_free_result ($myQuery);
2047
2048			// Increment for the Footer Stats
2049			$querycount++;
2050		}
2051
2052		// return Array
2053		return $arrIndexTriggers;
2054	}
2055
2056// --- End of Class!
2057}
2058
2059?>
2060