X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=framework%2Fmain%2Fclasses%2Fdatabase%2Fbackend%2Flfdb_legacy%2Fclass_CachedLocalFileDatabase.php;h=a4ef4ffcc0f6b8807063f2e19d006ca2d081fcc2;hb=3377ccc5ea2b0839cdb2506ecccc3b18cfb26d33;hp=37426c86d83891a0d974dc2706eb4ce4272617d7;hpb=edd70dac1d17d9bb60fd18bd89cd45a7526a61dd;p=core.git diff --git a/framework/main/classes/database/backend/lfdb_legacy/class_CachedLocalFileDatabase.php b/framework/main/classes/database/backend/lfdb_legacy/class_CachedLocalFileDatabase.php index 37426c86..a4ef4ffc 100644 --- a/framework/main/classes/database/backend/lfdb_legacy/class_CachedLocalFileDatabase.php +++ b/framework/main/classes/database/backend/lfdb_legacy/class_CachedLocalFileDatabase.php @@ -9,14 +9,18 @@ use Org\Mxchange\CoreFramework\Criteria\Local\LocalSearchCriteria; use Org\Mxchange\CoreFramework\Criteria\Storing\StoreableCriteria; use Org\Mxchange\CoreFramework\Database\Backend\BaseDatabaseBackend; use Org\Mxchange\CoreFramework\Database\Backend\DatabaseBackend; -use Org\Mxchange\CoreFramework\Factory\ObjectFactory; +use Org\Mxchange\CoreFramework\Database\Sql\SqlException; +use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory; use Org\Mxchange\CoreFramework\Filesystem\FileNotFoundException; use Org\Mxchange\CoreFramework\Generic\FrameworkException; +use Org\Mxchange\CoreFramework\Result\Database\BaseDatabaseResult; use Org\Mxchange\CoreFramework\Traits\Compressor\Channel\CompressorChannelTrait; use Org\Mxchange\CoreFramework\Traits\Handler\Io\IoHandlerTrait; // Import SPL stuff +use \InvalidArgumentException; use \SplFileInfo; +use \UnexpectedValueException; /** * Database backend class for storing objects in locally created files. @@ -29,7 +33,7 @@ use \SplFileInfo; * * @author Roland Haeder * @version 0.0.0 - * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2020 Core Developer Team + * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2022 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.shipsimu.org * @@ -81,13 +85,19 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac */ private $indexKey = '__idx'; + /** + * Cached file names based on table name to avoid "expensive" invocations + * of FrameworkConfiguration->getConfigEntry(). + */ + private $pathNames = []; + /** * The protected constructor. Do never instance from outside! You need to * set a local file path. The class will then validate it. * * @return void */ - protected function __construct () { + private function __construct () { // Call parent constructor parent::__construct(__CLASS__); } @@ -320,7 +330,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Then create the info file //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Creating info table for tableName=%s ...', $tableName)); $this->createTableInfoFile($dataSetInstance); - } elseif ((FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('db_update_primary_forced') == 'Y') && ($dataSetInstance->getPrimaryKey() != $this->tableInfo[$tableName]['primary'])) { + } elseif (FrameworkBootstrap::getConfigurationInstance()->isEnabled('db_update_primary_forced') && $dataSetInstance->getPrimaryKey() != $this->tableInfo[$tableName]['primary']) { // Set the array element //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Setting primaryKey=%s for tableName=%s ...', $dataSetInstance->getPrimaryKey(), $tableName)); $this->tableInfo[$tableName]['primary'] = $dataSetInstance->getPrimaryKey(); @@ -350,16 +360,23 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac * @param $tableName Name of the database table * @param $searchInstance Local search criteria class * @return $resultData Result data of the query + * @throws InvalidArgumentException If a parameter is not valid * @throws UnsupportedCriteriaException If the criteria is unsupported * @throws SqlException If an 'SQL error' occurs */ public function querySelect (string $tableName, LocalSearchCriteria $searchInstance) { - // The result is null by any errors + // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: tableName=%s,searchInstance=%s - CALLED!', $tableName, $searchInstance->__toString())); - $resultData = NULL; + if (empty($tableName)) { + // Throw IAE + throw new InvalidArgumentException('Parameter "tableName" is empty'); + } elseif (!isset($this->pathNames[$tableName])) { + // "Cache" is not present, so create and assign it + $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; + } - // Create full path name - $pathName = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; + // The result is null by any errors + $resultData = NULL; /* * A 'select' query is not that easy on local files, so first try to @@ -367,14 +384,14 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac */ try { // Get a directory pointer instance - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Getting directory_class for pathName=%s ...', $pathName)); - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Getting directory_class for this->pathNames[%s]=%s ...', $tableName, $this->pathNames[$tableName])); + $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // Initialize the result data, this need to be rewritten e.g. if a local file cannot be read - $resultData = array( - BaseDatabaseBackend::RESULT_INDEX_STATUS => self::RESULT_OKAY, - BaseDatabaseBackend::RESULT_INDEX_ROWS => [] - ); + $resultData = [ + BaseDatabaseResult::RESULT_NAME_STATUS => self::RESULT_OKAY, + BaseDatabaseResult::RESULT_NAME_ROWS => [] + ]; // Initialize limit/skip $limitFound = 0; @@ -382,7 +399,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac $idx = 1; // Read the directory with some exceptions - while (($fileInfoInstance = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { + while (($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: fileInfoInstance->extension=%s,this->fileExtension=%s', $fileInfoInstance->getExtension(), $this->getFileExtension())); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { @@ -390,7 +407,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Skipping fileInfoInstance->filename=%s ...', $fileInfoInstance->getFilename())); $directoryInstance->getDirectoryIteratorInstance()->next(); continue; - } // END - if + } // Read the file $dataArray = $this->getDataArrayFromFile($fileInfoInstance); @@ -404,40 +421,33 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Search in the criteria with FMFW (First Matches, First Wins) //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: data[]=%d', count($dataArray))); foreach ($dataArray as $key => $value) { - // Make sure value is not bool - assert(!is_bool($value)); - // Found one entry? - $isFound = (($isFound === true) && ($searchInstance->isCriteriaMatching($key, $value))); + $isFound = ($isFound && $searchInstance->isCriteriaMatching($key, $value)); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: key=%s,value[%s]=%s,isFound=%s', $key, gettype($value), $value, intval($isFound))); - } // END - foreach + } // Is all found? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: isFound=%d,limitFound=%d,limit=%d', intval($isFound), $limitFound, $searchInstance->getLimit())); if ($isFound === true) { // Shall we skip this entry? - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: searchInstance->skip=%d', $searchInstance->getSkip())); - if ($searchInstance->getSkip() > 0) { - // We shall skip some entries - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: skipFound=%s', $skipFound)); - if ($skipFound < $searchInstance->getSkip()) { - // Skip this entry - $skipFound++; - break; - } // END - if - } // END - if + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: searchInstance->skip=%d,skipFound=%d', $searchInstance->getSkip(), $skipFound)); + if ($searchInstance->getSkip() > 0 && $skipFound < $searchInstance->getSkip()) { + // Skip this entry + $skipFound++; + break; + } // Set id number //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: Setting dataArray[%s]=%d', $this->getIndexKey(), $idx)); $dataArray[$this->getIndexKey()] = $idx; // Entry found! - array_push($resultData[BaseDatabaseBackend::RESULT_INDEX_ROWS], $dataArray); + array_push($resultData[BaseDatabaseResult::RESULT_NAME_ROWS], $dataArray); // Count found entries up - //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: resultData[%s]()=%d', BaseDatabaseBackend::RESULT_INDEX_ROWS, count($resultData[BaseDatabaseBackend::RESULT_INDEX_ROWS]))); + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: resultData[%s]()=%d', BaseDatabaseResult::RESULT_NAME_ROWS, count($resultData[BaseDatabaseResult::RESULT_NAME_ROWS]))); $limitFound++; - } // END - if + } } else { // Throw an exception here throw new SqlException(array($this, sprintf('File '%s' contains invalid data.', $fileInfoInstance->getPathname()), self::DB_CODE_DATA_FILE_CORRUPT), self::EXCEPTION_SQL_QUERY); @@ -448,7 +458,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Advance to next entry $directoryInstance->getDirectoryIteratorInstance()->next(); - } // END - while + } // Close directory and throw the instance away $directoryInstance->closeDirectory(); @@ -498,11 +508,15 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac $this->setLastException($e); // Throw an SQL exception - throw new SqlException(array( + throw new SqlException([ $this, - sprintf('Cannot write data to table '%s', is the table created?', $dataSetInstance->getTableName()), + sprintf('Cannot write data to table '%s', is the table created? e=%s,e->message=%s', + $dataSetInstance->getTableName(), + $e->__toString(), + $e->getMessage() + ), self::DB_CODE_TABLE_UNWRITEABLE - ), + ], self::EXCEPTION_SQL_QUERY ); } @@ -516,16 +530,26 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac * * @param $dataSetInstance An instance of a StorableCriteria class * @return void + * @throws UnexpectedValueException If $tableName is empty * @throws SqlException If an SQL error occurs */ public function queryUpdateDataSet (StoreableCriteria $dataSetInstance) { - // Create full path name - $pathName = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $dataSetInstance->getTableName() . DIRECTORY_SEPARATOR; + // Get table name + $tableName = $dataSetInstance->getTableName(); + + // Is "cache" there? + if (empty($tableName)) { + // Should never be an empty string + throw new UnexpectedValueException('Class field dataSetInstance->tableName is empty'); + } elseif (!isset($this->pathNames[$tableName])) { + // "Cache" is not present, so create and assign it + $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; + } // Try all the requests try { // Get a file pointer instance - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); + $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // Initialize limit/skip $limitFound = 0; @@ -538,7 +562,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac $searchInstance = $dataSetInstance->getSearchInstance(); // Read the directory with some exceptions - while (($fileInfoInstance = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { + while (($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) && (($limitFound < $searchInstance->getLimit()) || ($searchInstance->getLimit() == 0))) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance->extension=' . $fileInfoInstance->getExtension() . ',this->getFileExtension()=' . $this->getFileExtension()); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { @@ -546,7 +570,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.extension=' . $fileInfoInstance->getExtension() . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); $directoryInstance->getDirectoryIteratorInstance()->next(); continue; - } // END - if + } // Open this file for reading $dataArray = $this->getDataArrayFromFile($fileInfoInstance); @@ -559,12 +583,9 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Search in the criteria with FMFW (First Matches, First Wins) foreach ($dataArray as $key => $value) { - // 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? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: isFound=' . intval($isFound)); @@ -577,8 +598,8 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Found entry, but skipping ...'); $skipFound++; break; - } // END - if - } // END - if + } + } // Entry found, so update it foreach ($searchArray as $searchKey => $searchValue) { @@ -589,7 +610,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Debug message + add/update it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: criteriaKey=' . $searchKey . ',criteriaValue=' . $searchValue); $dataArray[$searchKey] = $searchValue; - } // END - foreach + } // Write the data to a local file //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: Writing data[]=' . count($dataArray) . ' to ' . $fileInfoInstance->getPathname() . ' ...'); @@ -597,12 +618,12 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac // Count found entries up $limitFound++; - } // END - if - } // END - if + } + } // Advance to next entry $directoryInstance->getDirectoryIteratorInstance()->next(); - } // END - while + } // Close the file pointer $directoryInstance->closeDirectory(); @@ -627,9 +648,16 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac * * @param $tableName Name of the table we need the primary key from * @return $primaryKey Primary key column of the given table + * @throws InvalidArgumentException If a parameter is not valid * @todo Rename method to getPrimaryKeyFromTableInfo() */ public function getPrimaryKeyOfTable (string $tableName) { + // Validate parameter + if (empty($tableName)) { + // Throw IAE + throw new InvalidArgumentException('Parameter "tableName" is empty'); + } + // Default key is null $primaryKey = NULL; @@ -637,7 +665,7 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac if (isset($this->tableInfo[$tableName])) { // Then return the primary key $primaryKey = $this->tableInfo[$tableName]['primary']; - } // END - if + } // Return the column return $primaryKey; @@ -664,23 +692,30 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac * * @param $tableName Table name * @return $count Total rows of given table + * @throws InvalidArgumentException If a parameter is not valid */ public function countTotalRows (string $tableName) { + // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: tableName=' . $tableName . ' - CALLED!'); - - // Create full path name - $pathName = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; + if (empty($tableName)) { + // Throw IAE + throw new InvalidArgumentException('Parameter "tableName" is empty'); + } elseif (!isset($this->pathNames[$tableName])) { + // "Cache" is not present, so create and assign it + $this->pathNames[$tableName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('local_database_path') . $tableName . DIRECTORY_SEPARATOR; + } // Try all the requests + //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('CACHED-LFDB: this->pathNames[%s]=%s', $tableName, $this->pathNames[$tableName])); try { // Get a file pointer instance - $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', array($pathName)); + $directoryInstance = ObjectFactory::createObjectByConfiguredName('directory_class', [$this->pathNames[$tableName]]); // Initialize counter $count = 0; // Read the directory with some exceptions - while ($fileInfoInstance = $directoryInstance->readDirectoryExcept(array('.htaccess', 'info.' . $this->getFileExtension()))) { + while ($fileInfoInstance = $directoryInstance->readDirectoryExcept(['.gitkeep', 'info.' . $this->getFileExtension()])) { // Does the extension match? //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance->extension=' . $fileInfoInstance->getExtension() . ',this->getFileExtension()=' . $this->getFileExtension()); if ($fileInfoInstance->getExtension() !== $this->getFileExtension()) { @@ -688,12 +723,12 @@ class CachedLocalFileDatabase extends BaseDatabaseBackend implements DatabaseBac //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.extension=' . $fileInfoInstance->getExtension() . ',getFileExtension()=' . $this->getFileExtension() . ' - SKIPPED!'); // Skip this file! continue; - } // END - if + } // Count this row up //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('CACHED-LFDB: fileInfoInstance.pathname=' . $fileInfoInstance->getPathname() . ',getFileExtension()=' . $this->getFileExtension() . ' - COUNTED!'); $count++; - } // END - while + } } catch (FrameworkException $e) { // Catch all exceptions and store them in last error $this->setLastException($e);