Renamed to DatabaseBackend to get rid of word 'Interface' in an interface name as...
[core.git] / inc / classes / main / database / databases / class_LocalFileDatabase.php
index 2e28443090bf01b5ebfff2ebe9a36cb1d3a7b900..445b7f8d67481fd3e4e8e73647cace9df07f8721 100644 (file)
@@ -2,13 +2,17 @@
 /**
  * Database backend class for storing objects in locally created files.
  *
- * This class serializes objects and saves them to local files.
+ * This class serializes arrays stored in the dataset instance and saves them
+ * to local files. Every file (except 'info') represents a single line. Every
+ * directory within the 'db' (base) directory represents a table.
  *
- * @author             Roland Haeder <webmaster@ship-simu.org>
+ * A configurable 'file_io_class' is being used as "storage backend".
+ *
+ * @author             Roland Haeder <webmaster@shipsimu.org>
  * @version            0.0.0
- * @copyright  Copyright (c) 2007 - 2009 Roland Haeder, this is free software
+ * @copyright  Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2013 Core Developer Team
  * @license            GNU GPL 3.0 or any newer version
- * @link               http://www.ship-simu.org
+ * @link               http://www.shipsimu.org
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  */
-class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontendInterface {
-
-       // Constants for MySQL backward-compatiblity (PLEASE FIX THEM!)
-       const DB_CODE_TABLE_MISSING     = 0x100;
-       const DB_CODE_TABLE_UNWRITEABLE = 0x101;
-       const DB_CODE_DATA_FILE_CORRUPT = 0x102;
-
-       /**
-        * Save path for "file database"
-        */
-       private $savePath = '';
-
+class LocalFileDatabase extends BaseDatabaseBackend implements DatabaseBackend {
        /**
         * The file's extension
         */
-       private $fileExtension = "serialized";
+       private $fileExtension = 'serialized';
 
        /**
         * The last read file's name
@@ -51,19 +44,9 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
        private $lastContents = array();
 
        /**
-        * Wether the "connection is already up
-        */
-       private $alreadyConnected = false;
-
-       /**
-        * Last error message
-        */
-       private $lastError = '';
-
-       /**
-        * Last exception
+        * Whether the "connection is already up
         */
-       private $lastException = null;
+       private $alreadyConnected = FALSE;
 
        /**
         * Table information array
@@ -73,7 +56,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
        /**
         * Element for index
         */
-       private $indexKey = "__idx";
+       private $indexKey = '__idx';
 
        /**
         * The protected constructor. Do never instance from outside! You need to
@@ -81,78 +64,38 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         *
         * @return      void
         */
-       protected function __construct() {
+       protected function __construct () {
                // Call parent constructor
                parent::__construct(__CLASS__);
-
-               // Clean up a little
-               $this->removeNumberFormaters();
-               $this->removeSystemArray();
        }
 
        /**
-        * Create an object of LocalFileDatabase and set the save path for local files.
-        * This method also validates the given file path.
+        * Create an object of LocalFileDatabase and set the save path from
+        * configuration for local files.
         *
-        * @param               $savePath                                       The local file path string
-        * @param               $ioInstance                             The input/output handler. This
-        *                                                                      should be FileIoHandler
-        * @return      $dbInstance                             An instance of LocalFileDatabase
+        * @return      $databaseInstance       An instance of LocalFileDatabase
         */
-       public final static function createLocalFileDatabase ($savePath, FileIoHandler $ioInstance) {
+       public static final function createLocalFileDatabase () {
                // Get an instance
-               $dbInstance = new LocalFileDatabase();
+               $databaseInstance = new LocalFileDatabase();
 
-               // Set save path and IO instance
-               $dbInstance->setSavePath($savePath);
-               $dbInstance->setFileIoInstance($ioInstance);
+               // Get a new compressor channel instance
+               $compressorInstance = ObjectFactory::createObjectByConfiguredName('compressor_channel_class');
 
-               // "Connect" to the database
-               $dbInstance->connectToDatabase();
+               // Set the compressor channel
+               $databaseInstance->setCompressorChannel($compressorInstance);
 
-               // Return database instance
-               return $dbInstance;
-       }
+               // Get a file IO handler
+               $fileIoInstance = ObjectFactory::createObjectByConfiguredName('file_io_class');
 
-       /**
-        * Setter for save path
-        *
-        * @param               $savePath               The local save path where we shall put our serialized classes
-        * @return      void
-        */
-       public final function setSavePath ($savePath) {
-               // Secure string
-               $savePath = (string) $savePath;
-
-               // Set save path
-               $this->savePath = $savePath;
-       }
+               // ... and set it
+               $databaseInstance->setFileIoInstance($fileIoInstance);
 
-       /**
-        * Getter for save path
-        *
-        * @return      $savePath               The local save path where we shall put our serialized classes
-        */
-       public final function getSavePath () {
-               return $this->savePath;
-       }
-
-       /**
-        * Getter for last error message
-        *
-        * @return      $lastError      Last error message
-        */
-       public final function getLastError () {
-               return $this->lastError;
-       }
+               // "Connect" to the database
+               $databaseInstance->connectToDatabase();
 
-       /**
-        * Getter for last exception
-        *
-        * @return      $lastException  Last thrown exception
-        */
-       public final function getLastException () {
-               return $this->lastException;
+               // Return database instance
+               return $databaseInstance;
        }
 
        /**
@@ -166,21 +109,10 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                $this->lastFile = (string) $fqfn;
        }
 
-       /**
-        * Reset the last error and exception instance. This should be done after
-        * a successfull "query"
-        *
-        * @return      void
-        */
-       private final function resetLastError () {
-               $this->lastError = '';
-               $this->lastException = null;
-       }
-
        /**
         * Getter for last read file
         *
-        * @return      $lastFile               The last read file's name with full path
+        * @return      $lastFile       The last read file's name with full path
         */
        public final function getLastFile () {
                return $this->lastFile;
@@ -189,7 +121,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
        /**
         * Setter for contents of the last read file
         *
-        * @param               $contents               An array with header and data elements
+        * @param               $contents       An array with header and data elements
         * @return      void
         */
        private final function setLastFileContents (array $contents) {
@@ -231,14 +163,14 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         * @return      $dataArray
         */
        private function getDataArrayFromFile ($fqfn) {
-               // Get a file pointer
-               $fileInstance = FrameworkFileInputPointer::createFrameworkFileInputPointer($fqfn);
+               // Debug message
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Reading elements from database file ' . $fqfn . ' ...');
 
-               // Get the raw data and BASE64-decode it
-               $compressedData = base64_decode($fileInstance->readLinesFromFile());
+               // Init compressed data
+               $compressedData = $this->getFileIoInstance()->loadFileContents($fqfn);
+               $compressedData = $compressedData['data'];
 
                // Close the file and throw the instance away
-               $fileInstance->closeFile();
                unset($fileInstance);
 
                // Decompress it
@@ -247,6 +179,10 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                // Unserialize it
                $dataArray = unserialize($serializedData);
 
+               // Debug message
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Read ' . count($dataArray) . ' elements from database file ' . $fqfn . '.');
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataArray=' . print_r($dataArray, TRUE));
+
                // Finally return it
                return $dataArray;
        }
@@ -259,17 +195,21 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         * @return      void
         */
        private function writeDataArrayToFqfn ($fqfn, array $dataArray) {
-               // Get a file pointer instance
-               $fileInstance = FrameworkFileOutputPointer::createFrameworkFileOutputPointer($fqfn, 'w');
+               // Debug message
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Flushing ' . count($dataArray) . ' elements to database file ' . $fqfn . ' ...');
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataArray=' . print_r($dataArray, TRUE));
 
                // Serialize and compress it
                $compressedData = $this->getCompressorChannel()->getCompressor()->compressStream(serialize($dataArray));
 
+               // Write data
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Writing ' . strlen($compressedData) . ' bytes ...');
+
                // Write this data BASE64 encoded to the file
-               $fileInstance->writeToFile(base64_encode($compressedData));
+               $this->getFileIoInstance()->saveFile($fqfn, $compressedData, $this);
 
-               // Close the file pointer
-               $fileInstance->closeFile();
+               // Debug message
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: Flushing ' . count($dataArray) . ' elements to database file completed.');
        }
 
        /**
@@ -283,12 +223,12 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                $infoArray = array();
 
                // Create FQFN for getting the table information file
-               $fqfn = $this->getSavePath() . $dataSetInstance->getTableName() . '/info.' . $this->getFileExtension();
+               $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info');
 
                // Get the file contents
                try {
                        $infoArray = $this->getDataArrayFromFile($fqfn);
-               } catch (FileNotFoundException $e) {
+               } catch (FileIoException $e) {
                        // Not found, so ignore it here
                }
 
@@ -296,6 +236,21 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                return $infoArray;
        }
 
+       /**
+        * Generates an FQFN from given dataset instance and string
+        *
+        * @param       $dataSetInstance        An instance of a database set class
+        * @param       $rowName                        Name of the row
+        * @return      $fqfn                           The FQFN for this row
+        */
+       private function generateFqfnFromDataSet (Criteria $dataSetInstance, $rowName) {
+               // This is the FQFN
+               $fqfn = $this->getConfigInstance()->getConfigEntry('local_db_path') . $dataSetInstance->getTableName() . '/' . $rowName . '.' . $this->getFileExtension();
+
+               // Return it
+               return $fqfn;
+       }
+
        /**
         * Creates the table info file from given dataset instance
         *
@@ -304,7 +259,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         */
        private function createTableInfoFile (StoreableCriteria $dataSetInstance) {
                // Create FQFN for creating the table information file
-               $fqfn = $this->getSavePath() . $dataSetInstance->getTableName() . '/info.' . $this->getFileExtension();
+               $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info');
 
                // Get the data out from dataset in a local array
                $this->tableInfo[$dataSetInstance->getTableName()] = array(
@@ -317,6 +272,27 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                $this->writeDataArrayToFqfn($fqfn, $this->tableInfo[$dataSetInstance->getTableName()]);
        }
 
+       /**
+        * Updates the table info file from given dataset instance
+        *
+        * @param       $dataSetInstance        An instance of a database set class
+        * @return      void
+        */
+       private function updateTableInfoFile (StoreableCriteria $dataSetInstance) {
+               // "Cache" table name
+               $tableName = $dataSetInstance->getTableName();
+
+               // Create FQFN for creating the table information file
+               $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, 'info');
+
+               // Get the data out from dataset in a local array
+               $this->tableInfo[$tableName]['primary']      = $dataSetInstance->getPrimaryKey();
+               $this->tableInfo[$tableName]['last_updated'] = time();
+
+               // Write the data to the file
+               $this->writeDataArrayToFqfn($fqfn, $this->tableInfo[$tableName]);
+       }
+
        /**
         * Updates the primary key information or creates the table info file if not found
         *
@@ -324,16 +300,20 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         * @return      void
         */
        private function updatePrimaryKey (StoreableCriteria $dataSetInstance) {
+               // "Cache" table name
+               $tableName = $dataSetInstance->getTableName();
+
                // Get the information array from lower method
                $infoArray = $this->getContentsFromTableInfoFile($dataSetInstance);
 
                // Is the primary key there?
-               if (!isset($this->tableInfo['primary'])) {
+               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: tableInfo=' . print_r($this->tableInfo, TRUE));
+               if (!isset($this->tableInfo[$tableName]['primary'])) {
                        // Then create the info file
                        $this->createTableInfoFile($dataSetInstance);
-               } elseif (($this->getConfigInstance()->readConfig('db_update_primary_forced') === "Y") && ($dataSetInstance->getPrimaryKey() != $this->tableInfo['primary'])) {
+               } elseif (($this->getConfigInstance()->getConfigEntry('db_update_primary_forced') == 'Y') && ($dataSetInstance->getPrimaryKey() != $this->tableInfo[$tableName]['primary'])) {
                        // Set the array element
-                       $this->tableInfo[$dataSetInstance->getTableName()]['primary'] = $dataSetInstance->getPrimaryKey();
+                       $this->tableInfo[$tableName]['primary'] = $dataSetInstance->getPrimaryKey();
 
                        // Update the entry
                        $this->updateTableInfoFile($dataSetInstance);
@@ -353,30 +333,29 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         * Starts a SELECT query on the database by given return type, table name
         * and search criteria
         *
-        * @param       $resultType             Result type ("array", "object" and "indexed" are valid)
-        * @param       $tableName              Name of the database table
-        * @param       $criteria               Local search criteria class
-        * @return      $resultData             Result data of the query
+        * @param       $tableName                      Name of the database table
+        * @param       $searchInstance         Local search criteria class
+        * @return      $resultData                     Result data of the query
         * @throws      UnsupportedCriteriaException    If the criteria is unsupported
-        * @throws      SqlException                                    If an "SQL error" occurs
+        * @throws      SqlException                                    If an 'SQL error' occurs
         */
-       public function querySelect ($resultType, $tableName, LocalSearchCriteria $criteriaInstance) {
+       public function querySelect ($tableName, LocalSearchCriteria $searchInstance) {
                // The result is null by any errors
-               $resultData = null;
+               $resultData = NULL;
 
                // Create full path name
-               $pathName = $this->getSavePath() . $tableName . '/';
+               $pathName = $this->getConfigInstance()->getConfigEntry('local_db_path') . $tableName . '/';
 
-               // A "select" query is not that easy on local files, so first try to
-               // find the "table" which is in fact a directory on the server
+               // A 'select' query is not that easy on local files, so first try to
+               // find the 'table' which is in fact a directory on the server
                try {
                        // Get a directory pointer instance
                        $directoryInstance = FrameworkDirectoryPointer::createFrameworkDirectoryPointer($pathName);
 
                        // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read
                        $resultData = array(
-                               'status'        => "ok",
-                               'rows'          => array()
+                               BaseDatabaseBackend::RESULT_INDEX_STATUS => self::RESULT_OKAY,
+                               BaseDatabaseBackend::RESULT_INDEX_ROWS   => array()
                        );
 
                        // Initialize limit/skip
@@ -385,7 +364,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                        $idx = 1;
 
                        // Read the directory with some exceptions
-                       while (($dataFile = $directoryInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn", "info." . $this->getFileExtension()))) && ($limitFound < $criteriaInstance->getLimit())) {
+                       while (($dataFile = $directoryInstance->readDirectoryExcept(array('.', '..', '.htaccess', '.svn', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) {
                                // Does the extension match?
                                if (substr($dataFile, -(strlen($this->getFileExtension()))) !== $this->getFileExtension()) {
                                        // Skip this file!
@@ -394,41 +373,49 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
 
                                // Read the file
                                $dataArray = $this->getDataArrayFromFile($pathName . $dataFile);
+                               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',dataArray='.print_r($dataArray, TRUE));
 
                                // Is this an array?
                                if (is_array($dataArray)) {
+                                       // Default is nothing found
+                                       $isFound = TRUE;
+
                                        // Search in the criteria with FMFW (First Matches, First Wins)
                                        foreach ($dataArray as $key => $value) {
-                                               // Get criteria element
-                                               $criteria = $criteriaInstance->getCriteriaElemnent($key);
-
-                                               // Is the criteria met?
-                                               if ((!is_null($criteria)) && ($criteria == $value))  {
-
-                                                       // Shall we skip this entry?
-                                                       if ($criteriaInstance->getSkip() > 0) {
-                                                               // We shall skip some entries
-                                                               if ($skipFound < $criteriaInstance->getSkip()) {
-                                                                       // Skip this entry
-                                                                       $skipFound++;
-                                                                       break;
-                                                               } // END - if
+                                               // Make sure value is not bool
+                                               assert(!is_bool($value));
+
+                                               // Found one entry?
+                                               $isFound = (($isFound === TRUE) && ($searchInstance->isCriteriaMatching($key, $value)));
+                                               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: key=' . $key . ',value=' . $value . ',isFound=' . intval($isFound));
+                                       } // END - foreach
+
+                                       // Is all found?
+                                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: isFound=' . intval($isFound) . ',limitFound=' . $limitFound . ',limit=' . $searchInstance->getLimit());
+                                       if ($isFound === TRUE) {
+                                               // Shall we skip this entry?
+                                               if ($searchInstance->getSkip() > 0) {
+                                                       // We shall skip some entries
+                                                       if ($skipFound < $searchInstance->getSkip()) {
+                                                               // Skip this entry
+                                                               $skipFound++;
+                                                               break;
                                                        } // END - if
+                                               } // END - if
 
-                                                       // Set id number
-                                                       $dataArray[$this->getIndexKey()] = $idx;
+                                               // Set id number
+                                               $dataArray[$this->getIndexKey()] = $idx;
 
-                                                       // Entry found!
-                                                       $resultData['rows'][] = $dataArray;
+                                               // Entry found!
+                                               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: indexKey=' . $this->getIndexKey() . ',idx=' . $idx . ',dataArray=' . print_r($dataArray, TRUE));
+                                               array_push($resultData[BaseDatabaseBackend::RESULT_INDEX_ROWS], $dataArray);
 
-                                                       // Count found entries up
-                                                       $limitFound++;
-                                                       break;
-                                               } // END - if
-                                       } // END - foreach
+                                               // Count found entries up
+                                               $limitFound++;
+                                       } // END - if
                                } else {
                                        // Throw an exception here
-                                       throw new SqlException(array($this, sprintf("File &#39;%s&#39; contains invalid data.", $dataFile), self::DB_CODE_DATA_FILE_CORRUPT), self::EXCEPTION_SQL_QUERY);
+                                       throw new SqlException(array($this, sprintf('File &#39;%s&#39; contains invalid data.', $dataFile), self::DB_CODE_DATA_FILE_CORRUPT), self::EXCEPTION_SQL_QUERY);
                                }
 
                                // Count entry up
@@ -439,19 +426,17 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                        $directoryInstance->closeDirectory();
                        unset($directoryInstance);
 
-                       // Reset last error message and exception
-                       $this->resetLastError();
+                       // Reset last exception
+                       $this->resetLastException();
                } catch (PathIsNoDirectoryException $e) {
                        // Path not found means "table not found" for real databases...
-                       $this->lastException = $e;
-                       $this->lastError = $e->getMessage();
+                       $this->setLastException($e);
 
                        // So throw an SqlException here with faked error message
-                       throw new SqlException (array($this, sprintf("Table &#39;%s&#39; not found", $tableName), self::DB_CODE_TABLE_MISSING), self::EXCEPTION_SQL_QUERY);
+                       throw new SqlException (array($this, sprintf('Table &#39;%s&#39; not found', $tableName), self::DB_CODE_TABLE_MISSING), self::EXCEPTION_SQL_QUERY);
                } catch (FrameworkException $e) {
                        // Catch all exceptions and store them in last error
-                       $this->lastException = $e;
-                       $this->lastError = $e->getMessage();
+                       $this->setLastException($e);
                }
 
                // Return the gathered result
@@ -466,31 +451,25 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         * @throws      SqlException    If an SQL error occurs
         */
        public function queryInsertDataSet (StoreableCriteria $dataSetInstance) {
-               // Create full path name
-               $fqfn = sprintf("%s%s/%s.%s",
-                       $this->getSavePath(),
-                       $dataSetInstance->getTableName(),
-                       md5($dataSetInstance->getUniqueValue()),
-                       $this->getFileExtension()
-               );
-
                // Try to save the request away
                try {
+                       // Create full path name
+                       $fqfn = $this->generateFqfnFromDataSet($dataSetInstance, md5($dataSetInstance->getUniqueValue()));
+
                        // Write the data away
                        $this->writeDataArrayToFqfn($fqfn, $dataSetInstance->getCriteriaArray());
 
                        // Update the primary key
                        $this->updatePrimaryKey($dataSetInstance);
 
-                       // Reset last error message and exception
-                       $this->resetLastError();
+                       // Reset last exception
+                       $this->resetLastException();
                } catch (FrameworkException $e) {
                        // Catch all exceptions and store them in last error
-                       $this->lastException = $e;
-                       $this->lastError = $e->getMessage();
+                       $this->setLastException($e);
 
                        // Throw an SQL exception
-                       throw new SqlException (array($this, sprintf("Cannot write data to table &#39;%s&#39;", $tableName), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
+                       throw new SqlException(array($this, sprintf('Cannot write data to table &#39;%s&#39;, is the table created?', $dataSetInstance->getTableName()), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
                }
        }
 
@@ -503,7 +482,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         */
        public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) {
                // Create full path name
-               $pathName = $this->getSavePath() . $dataSetInstance->getTableName() . '/';
+               $pathName = $this->getConfigInstance()->getConfigEntry('local_db_path') . $dataSetInstance->getTableName() . '/';
 
                // Try all the requests
                try {
@@ -515,55 +494,68 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                        $skipFound = 0;
 
                        // Get the criteria array from the dataset
-                       $criteriaArray = $dataSetInstance->getCriteriaArray();
+                       $searchArray = $dataSetInstance->getCriteriaArray();
 
                        // Get search criteria
                        $searchInstance = $dataSetInstance->getSearchInstance();
 
                        // Read the directory with some exceptions
-                       while (($dataFile = $directoryInstance->readDirectoryExcept(array(".", "..", ".htaccess", ".svn", "info." . $this->getFileExtension()))) && ($limitFound < $searchInstance->getLimit())) {
+                       while (($dataFile = $directoryInstance->readDirectoryExcept(array('.', '..', '.htaccess', '.svn', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) {
                                // Does the extension match?
                                if (substr($dataFile, -(strlen($this->getFileExtension()))) !== $this->getFileExtension()) {
+                                       // Debug message
+                                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!');
                                        // Skip this file!
                                        continue;
-                               }
+                               } // END - if
 
                                // Open this file for reading
                                $dataArray = $this->getDataArrayFromFile($pathName . $dataFile);
+                               //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: dataFile=' . $dataFile . ',dataArray='.print_r($dataArray, TRUE));
 
                                // Is this an array?
                                if (is_array($dataArray)) {
+                                       // Default is nothing found
+                                       $isFound = TRUE;
+
                                        // Search in the criteria with FMFW (First Matches, First Wins)
                                        foreach ($dataArray as $key => $value) {
-                                               // Get criteria element
-                                               $criteria = $searchInstance->getCriteriaElemnent($key);
-
-                                               // Is the criteria met?
-                                               if ((!is_null($criteria)) && ($criteria == $value))  {
-
-                                                       // Shall we skip this entry?
-                                                       if ($searchInstance->getSkip() > 0) {
-                                                               // We shall skip some entries
-                                                               if ($skipFound < $searchInstance->getSkip()) {
-                                                                       // Skip this entry
-                                                                       $skipFound++;
-                                                                       break;
-                                                               } // END - if
+                                               // Make sure value is not bool
+                                               assert(!is_bool($value));
+
+                                               // Found one entry?
+                                               $isFound = (($isFound === TRUE) && ($searchInstance->isCriteriaMatching($key, $value)));
+                                       } // END - foreach
+
+                                       // Is all found?
+                                       if ($isFound === TRUE) {
+                                               // Shall we skip this entry?
+                                               if ($searchInstance->getSkip() > 0) {
+                                                       // We shall skip some entries
+                                                       if ($skipFound < $searchInstance->getSkip()) {
+                                                               // Skip this entry
+                                                               $skipFound++;
+                                                               break;
                                                        } // END - if
+                                               } // END - if
 
-                                                       // Entry found, so update it
-                                                       foreach ($criteriaArray as $criteriaKey => $criteriaValue) {
-                                                               $dataArray[$criteriaKey] = $criteriaValue;
-                                                       } // END - foreach
+                                               // Entry found, so update it
+                                               foreach ($searchArray as $searchKey => $searchValue) {
+                                                       // Make sure the value is not bool again
+                                                       assert(!is_bool($searchValue));
+                                                       assert($searchKey != $this->indexKey);
 
-                                                       // Write the data to a local file
-                                                       $this->writeDataArrayToFqfn($pathName . $dataFile, $dataArray);
+                                                       // Debug message + add/update it
+                                                       //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('DATABASE: criteriaKey=' . $searchKey . ',criteriaValue=' . $searchValue);
+                                                       $dataArray[$searchKey] = $searchValue;
+                                               } // END - foreach
 
-                                                       // Count it
-                                                       $limitFound++;
-                                                       break;
-                                               } // END - if
-                                       } // END - foreach
+                                               // Write the data to a local file
+                                               $this->writeDataArrayToFqfn($pathName . $dataFile, $dataArray);
+
+                                               // Count found entries up
+                                               $limitFound++;
+                                       } // END - if
                                } // END - if
                        } // END - while
 
@@ -573,15 +565,14 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
                        // Update the primary key
                        $this->updatePrimaryKey($dataSetInstance);
 
-                       // Reset last error message and exception
-                       $this->resetLastError();
+                       // Reset last exception
+                       $this->resetLastException();
                } catch (FrameworkException $e) {
                        // Catch all exceptions and store them in last error
-                       $this->lastException = $e;
-                       $this->lastError = $e->getMessage();
+                       $this->setLastException($e);
 
                        // Throw an SQL exception
-                       throw new SqlException (array($this, sprintf("Cannot write data to table &#39;%s&#39;", $dataSetInstance->getTableName()), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
+                       throw new SqlException(array($this, sprintf("Cannot write data to table &#39;%s&#39;, is the table created?", $dataSetInstance->getTableName()), self::DB_CODE_TABLE_UNWRITEABLE), self::EXCEPTION_SQL_QUERY);
                }
        }
 
@@ -594,7 +585,7 @@ class LocalFileDatabase extends BaseDatabaseFrontend implements DatabaseFrontend
         */
        public function getPrimaryKeyOfTable ($tableName) {
                // Default key is null
-               $primaryKey = null;
+               $primaryKey = NULL;
 
                // Does the table information exist?
                if (isset($this->tableInfo[$tableName])) {